必须检查 fopen 返回值是否为 null,否则后续操作会崩溃或静默失败;模式字符串大小写敏感;fclose 需配对调用且不可重复;二进制文件务必用 "rb"/"wb";c 与 c++ 文件流不可混用。

用 fopen 打开文件前,必须检查返回值是否为 NULL
这是最常被跳过的一步,但直接导致后续所有读写操作崩溃或静默失败。C++ 里调用 C 标准库函数不会抛异常,fopen 失败只返回 NULL,比如路径不存在、权限不足、磁盘满、文件正被其他进程独占锁定等。
实操建议:
- 每次调用
fopen后立刻判断:if (fp == NULL) { /* 处理错误,比如打印 <code>strerror(errno)*/ } - 不要假设“这个路径肯定存在”,尤其在跨平台时,Windows 路径分隔符是
,而 C 字符串里是转义符,得写成"C:\data\input.txt"或用正斜杠"C:/data/input.txt" -
fopen的第二个参数(模式字符串)大小写敏感:"r"可以,"R"不行;"wb"和"WB"行为不同,后者在某些旧 libc 上可能被截断为"W"导致未定义行为
fclose 必须配对调用,且不能重复关闭同一指针
忘记 fclose 会导致文件描述符泄漏,在长期运行程序(如服务、嵌入式)中积累后,fopen 会开始失败,错误码通常是 EMFILE(打开文件过多)或 ENFILE(系统级限制)。
实操建议:
- 每个
fopen对应一个明确的fclose,最好在同一个作用域内完成,或用 RAII 封装(虽然这是 C 风格 API,但在 C++ 中可自己写轻量 wrapper) - 关闭后立即将指针设为
NULL:fclose(fp); fp = NULL;,避免后续误用已释放的FILE* - 不要对同一个
FILE*调用两次fclose—— 这不是幂等操作,第二次会触发未定义行为,常见表现是段错误或内存破坏
文本模式和二进制模式的区别直接影响换行和读写一致性
在 Windows 上,"r" 和 "rb" 表现差异明显:前者自动把
转成单个
供程序读取,写入时再转回
;后者原样传递字节。如果用 "r" 读二进制文件(如图片、ZIP),内容会被损坏;用 "wb" 写文本则不会加额外换行转换,可能导致编辑器显示异常。
实操建议:
- 处理纯文本(如配置、日志)用
"r"/"w";处理图像、音频、序列化数据、网络协议包等,一律用"rb"/"wb" - 跨平台项目中,别依赖文本模式的换行转换 —— 容易在 Linux/macOS 下漏掉
判断,或在 Windows 下多读一个字节 -
"a"(追加)模式下,即使调用fseek也无法改变写入位置,所有写入始终在文件末尾,这点和"w"不同
C++ 中混用 std::fstream 和 fopen 会引发未定义行为
两者底层都依赖系统文件描述符,但缓冲、锁机制、关闭时机完全独立。比如用 fopen 打开一个文件,又用 std::ofstream 打开同一路径,结果不可预测:可能其中一个操作失败,也可能数据错乱,甚至程序崩溃。
实操建议:
- 一个文件对象生命周期内,只用一套 I/O 接口:要么全用 C 风格(
fopen/fread/fclose),要么全用 C++ 风格(std::ifstream/std::ofstream) - 不要试图把
FILE*传给 C++ 流(比如用std::fdopen是可行的,但那是另一套桥接逻辑,不属于基础用法) - 注意
stdout/stdin/stderr是预定义的FILE*,它们和std::cout/std::cin默认同步,但若调用std::ios_base::sync_with_stdio(false),就不再保证与 C 流一致输出顺序
文件句柄生命周期、模式语义、跨平台换行处理这三块,最容易在调试后期才暴露——因为前期小样本测试总能“碰巧”跑通。
13万字C语言保姆级教程(深入):立即使用
在学习笔记中,你将探索c语言的核心概念和高级技巧!











