1. 缓冲的来源
通常,文件数据的读写效率远低于内存读写效率。如果不存在缓冲,CPU 在读取文件数据时需要先发送指令给 DMA(专门处理 IO 数据读写的硬件),而后 DMA 从文件数据读取一个 0 到内存,CPU 再处理该数据。此过程 CPU 全程参加,极大浪费了 CPU 资源。
文件流缓冲的目的正是降低 IO 对于 CPU 占用。如图所示,在添加缓冲区后,DMA 可以将文件数据先读取到缓冲区中,等待缓冲区满时,再通知 CPU,此时 CPU 将缓冲区的数据读取并处理。
2. 设置缓冲区大小
可使用函数 setbuf() 和 setvbuf() 设置缓冲区。
setbuf() 函数接收两个参数,一是文件指针,二是缓冲区的指针。setbuf() 的缺点在于无法设置缓冲区大小。原因是传入参数为缓冲区指针的情况下,仅知道缓冲区的起始地址,并不知道缓冲区的结束地址,故无法设置其大小。通常情况下,编译器设定 setbuf() 函数的缓冲区大小为 BUFSIZ 。
char buf[BUFSIZ];
setbuf(file, buf);
setvbuf() 函数可以设置缓冲区大小。其接收四个参数,前两个参数与 setbuf() 函数相同,第三个参数为缓冲方式( _IOFBF 全量缓冲,_IOLBF 按行缓冲,_IONBF 禁用缓冲),第四个参数为缓冲区大小。
char buf[8192];
setvbuf(file, buf, _IOFBF, 8192);
3. 缓冲区的生命周期和清空
缓冲区的生命周期需要大于等于文件流的生命周期。通常可以利用 malloc() 函数,指定一块内存当做缓冲区,等到文件流关闭后再释放该内存。
清空缓存区需要利用函数 fflush() 函数,其可以将缓冲区数据直接读取,不必等缓冲区结束。以控制台输出对应的 stdout 文件指针为例,通常在 debug 代码过程中,控制台不会输出任何值,如下图所示:
添加 fflush() 函数后,结果如下图所示。可以发现,控制台将缓冲区的所有内容进行了输出。
void TestFflush(){
FILE *file = fopen("CMakeLists.txt", "r");
if(file){
char buf[8192];
setvbuf(file, buf, _IOFBF, 8192);
puts("Open successfully");
int err = ferror(file); // 文件读取是否出错
int eof = feof(file); // 文件读取是否结束
printf("err: %d\n", err); // 0,说明没有出错
printf("eof: %d\n", eof); // 0,说明没有结束
fflush(stdout); // 清空缓冲区
fclose(file); // 文件打开后,必须关闭
}else{
perror("fopen"); // 打印自带的提示错误信息
}
}