疑问:fopen函数在文件已打开时是否返回NULL指针?实测w模式未返回NULL
你的假设其实是对C标准和操作系统文件行为的常见误解——C语言标准并没有规定当文件已被打开时,再次调用fopen会返回NULL。实际行为取决于你使用的操作系统和编译器实现,这也是你测试时没看到错误的核心原因。
先明确fopen("w")的标准行为
根据C标准,用"w"模式打开文件时会执行两个核心操作:
- 如果文件已存在,**立即截断(清空)**文件内容;
- 打开文件以进行写入操作(如果文件不存在则创建)。
这里的关键是:操作系统默认允许多个文件流(或进程)同时打开同一个文件,哪怕其中已经有一个是写入模式打开的。你的测试环境是Windows(mingw32和TDM-GCC都是Windows平台的编译器),Windows下fopen默认的文件共享模式是_SH_DENYNO——也就是允许其他句柄读写这个文件,所以第二次fopen调用完全合法,会成功返回有效的文件指针,同时再次截断文件。
关于你印象里的C++报错
你提到C中重复打开会报错,这其实是特定标准库实现的行为,不是C强制要求的。比如在Windows的MSVC标准库中,当你用std::ofstream默认打开文件时,它会使用不允许其他写入操作的共享模式——如果文件已经被其他句柄以写入模式打开,ofstream的打开操作就会失败,设置failbit,这让你产生了"C++会报错"的印象,但这不是跨平台的通用行为。
补全你的测试代码并验证
你提供的测试代码没写完,补全后大概是这样:
#include<stdio.h> int main() { FILE *fp1, *fp2; fp1 = fopen("file1.txt", "w"); fp2 = fopen("file1.txt", "w"); if (fp1 == NULL || fp2 == NULL) { printf("Open failed!\n"); return 1; } fprintf(fp1, "Hello from fp1\n"); fprintf(fp2, "Hello from fp2\n"); fclose(fp1); fclose(fp2); return 0; }
运行这段代码后,你会发现file1.txt里最终只有Hello from fp2——因为第二个fopen调用截断了文件,覆盖了第一个流写入的内容(如果没有手动刷新缓冲区,第一个流的内容可能还没写入磁盘就被截断了)。
如果想让重复打开失败怎么办?
如果需要确保同一时间只有一个文件流能打开某个文件进行写入,你需要依赖操作系统的文件锁定机制:
- 在Windows下,可以使用
_fsopen函数,指定_SH_DENYWR共享模式,这样当文件已被写入模式打开时,后续打开会失败; - 在Linux下,可以用
flock或fcntl函数给文件加锁,阻止其他进程打开。
内容的提问来源于stack exchange,提问作者Rajesh

