磁盘满时std::ofstream不立即设failbit/badbit,需在写失败后紧邻检查errno是否为enospc(linux/macos)或_doserrno映射值(windows),good()等状态检查无法预测磁盘满。

检查 failbit 和 badbit 不足以判断磁盘满
磁盘写满时,C++ 标准库的 std::ofstream 通常不会立即设置 failbit 或 badbit —— 它可能先缓存数据,直到 flush() 或析构时才真正尝试落盘。此时才可能触发失败,但错误已被“延迟暴露”。更关键的是:failbit 可能由格式错误、权限不足、路径不存在等多种原因引起;badbit 多指向底层流缓冲区崩溃(如 write() 系统调用返回 EIO),而磁盘满实际常返回 ENOSPC,它往往被封装在 errno 中,标准流对象并不直接暴露它。
必须结合 errno 和系统级检查才能可靠识别 ENOSPC
当 ofstream 写入失败(例如 !os 为真或 os.fail() 返回 true)后,应立刻检查 errno 值是否为 ENOSPC。但注意:不同平台对 errno 的保留行为不一致,需在失败后**紧邻地**读取,且不能有其他可能修改 errno 的系统调用穿插。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
-
std::ofstream的clear()会重置状态位,但**不会清空errno**;而close()或析构可能触发二次写入,覆盖errno - Linux/macOS 下可直接用
#include <cerrno></cerrno>和ENOSPC;Windows 需用_doserrno并映射(_doserrno == ERROR_HANDLE_DISK_FULL或ERROR_DISK_FULL) - 示例逻辑:
std::ofstream os("data.bin", std::ios::binary); os.write(buf, size); if (!os) { int saved_errno = errno; // 必须立刻保存 if (saved_errno == ENOSPC) { // 磁盘已满 } }
避免依赖 good() 或 operator bool() 做提前预判
good() 是 !fail() && !bad() && !eof() 的组合,它只反映流对象自身的状态,完全无法预测下一次写是否成功——尤其对磁盘满这种外部资源限制,流对象无感知。试图用 os.seekp(0, std::ios::end); os.tellp() 推算剩余空间更是危险:文件系统预留空间、配额、稀疏文件、快照机制都会让结果失真。
- 不要写
if (os.good()) os 来“预防”失败——这毫无意义 - 不要用
statfs()/GetDiskFreeSpaceEx()检查剩余空间再写,因为两次调用之间可能被其他进程占满 - 正确做法是:写 → 检查失败 → 查
errno→ 分情况处理(ENOSPC就清理/告警,EACCES就改权限,ENOSPC和EIO的处理策略完全不同)
析构时隐式 close() 导致 errno 覆盖是高频坑点
如果未显式调用 close(),ofstream 析构时会自动 flush 并 close。此时若发生 ENOSPC,errno 就在析构内部被覆盖,你根本来不及捕获。因此:
- 务必显式调用
os.close(),并在其后**立刻**检查errno - 或使用 RAII 封装,在自定义析构中捕获失败(但需确保异常安全,避免在析构中抛异常)
- 更稳妥的做法是:每次
write()后都检查状态 +errno,而不是等到最后 - Linux 下还可配合
sync_file_range()或fdatasync()强制落盘并捕获早期错误,但会牺牲性能
errno 直接靠 C++ 流接口判断,等于在盲区里开车。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










