2024-2025-1 20241301 《计算机基础与程序设计》第十二周学习总结
|这个作业属于哪个课程|2024-2025-1-计算机基础与程序设计|
|这个作业要求在哪里|2024-2025-1计算机基础与程序设计第一周作业|
|这个作业的目标|<复习知识,巩固基础>|
|作业正文|https://www.cnblogs.com/HonJo/p/18607135|
一、教材学习内容
(一)文件
在C语言中,文件操作是程序设计中的一个重要部分,它允许程序创建、读取、写入和处理文件。C语言提供了一组标准的库函数,这些函数定义在stdio.h
头文件中,用于执行文件的各种操作。
文件结构
C语言中,文件以流的形式存在,这意味着数据是按顺序处理的,可以是字符、整数或字符串。文件以只读、只写或读写模式打开。
文件指针
每个打开的文件都与一个文件指针(FILE*
类型)关联,该指针指向一个FILE
结构,其中包含了文件的状态信息。
打开文件
使用fopen
函数打开文件,并返回一个文件指针:
FILE *fp;
// 以只读模式打开文件
fp = fopen("example.txt", "r");
// 以写入模式打开文件
fp = fopen("example.txt", "w");
// 以追加模式打开文件
fp = fopen("example.txt", "a");
// 以读/写模式打开文件
fp = fopen("example.txt", "r+");
模式字符串可以包含以下字符:
r
:只读模式打开文件。w
:只写模式打开文件,如果文件存在,则截断为零长度,如果文件不存在,则创建新文件。a
:追加模式打开文件,写操作会在文件末尾追加数据,如果文件不存在,则创建新文件。b
:二进制模式,可以与其他模式组合使用(如rb
、wb
)。+
:更新模式,允许读写。
读取文件
使用fgetc
、fgets
、fread
等函数从文件中读取数据:
// 读取单个字符
char ch = fgetc(fp);
// 读取一行
char buffer[100];
fgets(buffer, sizeof(buffer), fp);
// 读取指定数量的元素
int items_read = fread(buffer, sizeof(char), 100, fp);
写入文件
使用fputc
、fputs
、fwrite
等函数向文件写入数据:
// 写入单个字符
fputc('A', fp);
// 写入字符串
fputs("Hello, World!", fp);
// 写入指定数量的元素
int items_written = fwrite(buffer, sizeof(char), 100, fp);
文件定位
使用rewind
、fseek
、ftell
等函数在文件中定位:
// 将文件指针重置到文件开头
rewind(fp);
// 移动文件指针到指定位置
fseek(fp, offset, whence);
// 获取当前文件指针的位置
long position = ftell(fp);
whence
参数可以是SEEK_SET
(文件开头)、SEEK_CUR
(当前位置)、SEEK_END
(文件末尾)。
关闭文件
使用fclose
函数关闭文件,并释放资源:
fclose(fp);
错误检查
使用ferror
和clearerr
函数检查和清除文件错误:
if (ferror(fp)) {
// 处理错误
}
clearerr(fp); // 清除错误标志
文件结束检测
使用feof
函数检查是否到达文件末尾:
if (feof(fp)) {
// 到达文件末尾
}
临时文件
使用tmpfile
创建临时文件,该文件在关闭时会自动删除:
FILE *temp = tmpfile();
二进制文件操作
C语言中的文件操作函数也可以用于二进制文件,只需在模式字符串中添加b
字符。
文件操作是C语言中处理数据持久化的基本方式,掌握这些操作对于编写能够读写文件的程序至关重要。
(二)内存管理
内存管理是操作系统中的一个重要功能,它涉及到如何有效地分配和回收内存资源。以下是三种主要的内存管理方式:
单块内存管理(Single-Block Memory Management)
单块内存管理是一种最简单的内存管理方式,它将整个内存分为两个区域:系统区和用户区。在这种管理方式中,内存被看作是一个单一的大块,程序在执行时会占用整个内存块。这种方式适用于单用户、单任务的操作系统,如早期的CP/M和DOS系统。它的优点在于易于管理,但也存在问题,例如对内存需求小的程序会造成浪费,且程序必须全部装入内存才能运行。
分区内存管理(Partition Memory Management)
分区内存管理将内存分为多个固定大小或动态大小的分区。这些分区可以固定不变(固定分区法)或者根据需要动态分配和回收(动态分区法)。
- 固定分区法:内存被划分为若干个固定大小的分区,这些分区的大小在系统启动时确定,不会改变。这种方式的优点是管理简单,但由于分区大小固定,可能会导致内存浪费或不足。
- 动态分区法:内存被划分为可以动态变化大小的分区。操作系统根据进程的实际需要动态地分配和回收内存分区。这种方式可以更有效地利用内存,减少浪费,但也更复杂,需要操作系统进行更多的管理工作。
页式内存管理(Page Memory Management)
页式内存管理是将进程的逻辑地址空间和物理内存空间都分割成固定大小的单元,分别称为页面和物理块(页框)。进程的页面被装入物理内存中的物理块,这些物理块不需要连续,从而实现了内存的非连续分配。
- 基本分页存储管理:进程的所有页面在运行前必须全部装入物理内存中。
- 请求分页存储管理:只有当页面被实际访问时,才将其从辅助存储器装入物理内存中。这种方式允许进程的逻辑地址空间大于物理内存的大小,因为并不是所有的页面都需要同时驻留在内存中。
页式内存管理通过页表来维护逻辑地址和物理地址之间的映射关系,提高了内存的利用率,并引入了虚拟内存的概念。这种方式允许系统运行的进程的总大小超过物理内存的大小,通过页面交换(页面从内存移动到磁盘)来管理内存。
这三种内存管理方式各有优缺点,操作系统会根据具体的需求和环境选择最合适的内存管理策略。
(三)CPU调度
CPU调度(CPU Scheduling)是操作系统中的一个核心功能,它负责决定哪个进程应该获得CPU的使用权。由于在多道程序设计环境中,通常有多个进程同时竞争CPU资源,因此CPU调度算法需要高效且公平地管理这些进程,以优化系统性能和响应时间。以下是一些关键点:
CPU调度的类型
-
长程调度(Long-term Scheduling):
- 也称为作业调度,发生在批处理系统中。
- 决定哪些作业应该被加载到内存中执行。
- 通常基于作业的优先级、预计运行时间和资源需求。
-
短程调度(Short-term Scheduling):
- 也称为进程调度,是最常见的CPU调度类型。
- 决定哪个就绪状态下的进程应该获得CPU。
- 直接影响系统的响应时间和吞吐量。
-
中等调度(Medium-term Scheduling):
- 存在于具有虚拟内存的系统中。
- 管理内存中的进程,可能涉及挂起(swap out)和唤醒(swap in)进程。
CPU调度算法
-
先来先服务(FCFS, First-Come, First-Served):
- 按照进程到达的顺序进行调度。
- 简单但可能导致较长的等待时间,特别是在进程服务时间差异较大时。
-
最短作业优先(SJF, Shortest Job First):
- 优先调度预计运行时间最短的进程。
- 可以减少平均等待时间,但可能导致“饥饿”现象,即长作业可能长时间得不到调度。
-
最短剩余时间优先(SRTF, Shortest Remaining Time First):
- 是SJF的变种,考虑的是当前运行时间最短的进程。
- 同样可能导致饥饿现象。
-
轮转调度(RR, Round Robin):
- 将所有就绪进程按照到达顺序排成队列,并为每个进程分配一个时间片。
- 时间片用完后,如果进程尚未完成,则被放回队列末尾。
-
优先级调度(Priority Scheduling):
- 根据进程的优先级进行调度。
- 高优先级的进程先于低优先级的进程执行。
-
多级队列调度(Multilevel Queue Scheduling):
- 将就绪队列分为多个级别,每个级别有自己的调度算法。
-
多级反馈队列调度(Multilevel Feedback Queue Scheduling):
- 结合了轮转和优先级调度的特点,允许进程在不同队列间移动。
CPU调度的目标
-
公平性(Fairness):
- 确保所有进程都能获得合理的CPU时间。
-
响应时间(Response Time):
- 减少用户请求和系统响应之间的延迟。
-
吞吐量(Throughput):
- 提高单位时间内完成的进程数量。
-
周转时间(Turnaround Time):
- 减少从提交到完成的总时间。
-
资源利用率(Resource Utilization):
- 最大化CPU和其他资源的利用率。
-
优先级满足(Priority Satisfaction):
- 满足不同进程的优先级要求。
CPU调度是操作系统设计中的一个复杂问题,需要根据具体的应用场景和系统需求来选择合适的调度算法。
二、教材学习中遇到的问题
(一)运用文件需要注意的问题
在C语言中使用文件时,需要注意以下几个问题:
-
文件指针的正确打开:
- 使用
fopen
函数打开文件时,需要检查返回值是否为NULL
,以确保文件成功打开。 - 选择合适的模式(如
"r"
、"w"
、"a"
、"r+"
等)来打开文件。
- 使用
-
文件路径和权限:
- 确保程序有足够的权限来打开指定的文件。
- 确保提供的文件路径是正确的,并且文件存在于该路径下。
-
错误处理:
- 在文件操作过程中,应该检查每个文件操作函数的返回值,以处理可能发生的错误。
-
内存管理:
- 当使用
fscanf
、fgets
等函数读取数据时,确保目标缓冲区足够大,以避免缓冲区溢出。
- 当使用
-
文件关闭:
- 在文件操作完成后,使用
fclose
函数关闭文件,以释放系统资源。 - 确保在程序的任何退出点(如
return
语句之前)关闭文件。
- 在文件操作完成后,使用
-
数据同步:
- 使用
fflush
函数可以强制将缓冲区中的数据写入到文件中,这对于需要立即写入数据的场景很有用。
- 使用
-
文件读写位置:
- 使用
fseek
、rewind
、ftell
等函数来管理文件读写的位置。
- 使用
-
文件缓冲区:
- 了解文件缓冲区的行为,特别是在多次读写操作时。
-
文件结束检测:
- 使用
feof
函数检测是否到达文件末尾,但要注意feof
只有在尝试读取文件末尾之后才返回非零值。
- 使用
-
字符编码和换行符:
- 了解不同操作系统中文件的字符编码和换行符(如
\n
在Unix/Linux中和\r\n
在Windows中)。
- 了解不同操作系统中文件的字符编码和换行符(如
-
二进制文件操作:
- 当处理二进制文件时,使用
fread
和fwrite
等函数,并确保数据类型的对齐。
- 当处理二进制文件时,使用
-
临时文件:
- 如果需要创建临时文件,可以使用
tmpfile
函数,并在使用完毕后关闭和删除。
- 如果需要创建临时文件,可以使用
-
文件锁定:
- 在多线程或多进程环境中,可能需要对文件进行锁定以避免竞争条件。
-
文件属性:
- 使用
stat
函数等可以获取文件的属性,如大小、权限、最后修改时间等。
- 使用
-
跨平台兼容性:
- 考虑到不同操作系统间的差异,编写跨平台的文件操作代码时需要特别注意。
-
文件删除和重命名:
- 使用
remove
和rename
函数时要小心,因为这些操作是不可逆的。
- 使用
-
文件I/O效率:
- 对于大文件或频繁的文件操作,考虑I/O效率,可能需要使用缓冲或异步I/O。
-
信号和中断处理:
- 在信号处理函数中进行文件操作时要小心,因为某些文件操作可能不是信号安全的。
遵循这些注意事项可以帮助你更安全、更有效地在C语言中处理文件。
三、基于AI的学习