FILE流缓冲区工作原理及文件复制中缓冲区直接传输的疑问
好问题!你已经准确理解了fopen会为文件创建内部读写缓冲区,那为什么示例里的_copy函数非要用临时数组作为fread和fwrite的中转,而不能直接把源文件的内部缓冲区数据“搬运”到目标文件的内部缓冲区呢?这里有几个核心原因:
标准库的封装性限制:C标准库中
FILE结构体的内部缓冲区属于私有实现细节,C标准并没有规定开发者可以直接访问或操作这些缓冲区。不同编译器(比如GCC、MSVC)甚至同一编译器的不同版本,对FILE的内部结构定义都可能不同。如果硬要去读写这些内部缓冲区,你的代码会彻底失去可移植性,而且很容易因为库的更新而崩溃。fread和fwrite的设计就是通过用户提供的缓冲区来隔离这些内部细节,保证代码的兼容性。数据传输的可控性与灵活性:使用临时数组中转,你可以完全掌控每次传输的数据量(比如示例中定义的
BUFF大小),还能在传输过程中轻松加入额外逻辑——比如修改数据内容、统计传输的字节数、过滤特定数据等。如果直接在内部缓冲区之间传输,你就完全失去了这种控制权,无法干预数据的流动过程。缓冲区的状态同步需求:每个文件的内部缓冲区都和文件的读写状态(比如当前文件偏移量、错误标记、EOF状态)紧密绑定。源文件的内部缓冲区可能还没被填满,或者目标文件的内部缓冲区还没准备好接收新数据,直接拷贝内部缓冲区会打破这些状态的同步机制。而
fread和fwrite会自动处理这些状态:fread负责将磁盘数据读取到用户缓冲区(同时管理源文件的内部缓冲和偏移量),fwrite负责将用户缓冲区的数据写入目标文件的内部缓冲(并在缓冲区满时自动刷写到磁盘)。函数接口的设计逻辑:
fread和fwrite的接口设计本身就围绕用户缓冲区展开。fread需要你提供一个用户空间的指针来存储读取的数据,fwrite需要你提供要写入数据的指针——这两个函数从设计上就没有支持直接在两个FILE流之间传输数据的能力,因为这种场景完全可以通过用户缓冲区中转实现,且灵活性更高。
补充一点:有些操作系统提供了底层系统调用(比如Linux的splice)可以直接在文件描述符之间传输数据,跳过用户空间的拷贝,但这属于系统级功能,C标准库并没有封装这类接口。你示例中的代码是标准C环境下最稳妥、可移植性最好的文件拷贝实现方式。
你提供的示例代码
/* 示例:copyfile.exe xxxxx.txt zzzzzz.txt */ #include <stdio.h> #include <stdlib.h> #define BUFF 8192 void _copy(FILE *source, FILE *destination); int main(int argc, char *argv[]) { FILE *fp1, *fp2; // fp1 源文件指针// fp2 复制文件指针 if (argc !=3 ) // 命令行必须包含3个参数 { fprintf(stderr, "Usage: %s (source file) (copy file)\n", argv[0][0]); exit(EXIT_FAILURE); } if ((fp1 = fopen(argv[1], "rb")) == NULL) // 打开源文件 { fprintf(stderr, "Could not open %s\n",argv[1]); exit(EXIT_FAILURE); } if((fp2 = fopen(argv[2], "ab+")) == NULL) // 打开目标文件 { fprintf(stderr, "could not create %s \n",argv[2]); exit(EXIT_FAILURE); } if( setvbuf(fp1,NULL, _IOFBF, BUFF) != 0) // 设置源文件缓冲区 { fputs("Can't create output buffer\n", stderr); exit(EXIT_FAILURE); } if( setvbuf(fp2,NULL, _IOFBF, BUFF) != 0) // 设置目标文件缓冲区 { fputs("Can't create input buffer\n", stderr); exit(EXIT_FAILURE); } _copy(fp1, fp2); if (ferror(fp1)!=0) fprintf(stderr, "Error reading file %s\n", argv[1]); if(ferror(fp2)!=0) fprintf(stderr, "Error writing file %s\n",argv[2]); printf("Done coping %s (source) to %s (destination) \n",argv[1], argv[2]); fclose(fp1); fclose(fp2); return (0); } void _copy(FILE *source, FILE *destination) { size_t bytes; static char temp[BUFF]; while((bytes = fread(temp,sizeof(char),BUFF,source))>0) fwrite(temp,sizeof(char),bytes,destination); }
内容的提问来源于stack exchange,提问作者topcat

