用 ofstream 写二进制文件必须加 ios::binary,否则 Windows 下会将 \n 自动转为 \r\n,导致二进制数据错位、读取崩溃;正确写法:ofstream ofs("data.bin", ios::binary);

用 ofstream 写二进制文件必须加 ios::binary
不加这个标志,ofstream 默认按文本模式写入,会在 Windows 上把 \n 自动转成 \r\n,导致二进制数据错位。比如写一个 int 数组,本该是 4 字节连续存储,结果中间插了额外字节,后续读取直接崩溃。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 打开文件时务必显式指定
ios::binary,例如:ofstream ofs("data.bin", ios::binary); - 避免用
操作符写原始数据——它走格式化输出,只适合字符串或数字文本;二进制写必须用write() - 写结构体前确认没有内存对齐填充干扰:用
#pragma pack(1)或alignas(1)控制,否则sizeof(MyStruct)≠ 实际字段总和
用 ifstream 读二进制文件要检查 gcount() 和 fail()
很多人只调 read() 就以为完事了,但文件末尾提前结束、磁盘错误、权限不足都会让读取字节数少于预期,而 read() 本身不抛异常,也不自动置 failbit —— 它只在尝试读超限后才设 failbit,但已读部分仍算“成功”。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 每次
read()后立刻查ifs.gcount(),确保等于期望字节数 - 同时检查
ifs.fail()和ifs.eof():前者表示底层 I/O 错误,后者仅表示到了流末尾(未必是错误) - 别依赖
while (ifs)循环读块——最后一次读可能只读到部分数据却未触发fail(),导致循环多跑一次
write() 和 read() 的参数必须是 char* 和字节数
这两个函数不认类型,只认内存地址和长度。传 &my_int 是对的,但传 &my_vector 或 my_string.c_str() 就危险:前者是对象头地址,不是数据起始;后者没保证以 \0 结尾,且长度得自己算。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 写基本类型直接用地址:
ofs.write(reinterpret_cast(&x), sizeof(x)); - 写数组注意长度单位:
ofs.write(reinterpret_cast(arr), sizeof(int) * n); - 写
std::vector最安全:ofs.write(vec.data(), vec.size());(C++11 起data()非空时保证有效) - 绝对不要对
std::string直接data()写——它不包含结尾\0,但如果你存的是纯二进制 blob,且明确知道长度,那可以;否则容易漏掉最后几个字节
跨平台读写要注意字节序和结构体布局
同一个 struct 在 x86 和 ARM 上可能字段偏移不同,int 在小端机上是低字节在前,大端机则相反。如果文件要在不同架构间交换,光靠 write() 原样 dump 会出问题。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 网络传输或跨平台存档,优先用协议缓冲(protobuf)或手动序列化,而不是裸
write() - 若坚持用二进制直写,读写两端必须用相同编译器、相同 ABI、相同
#pragma pack设置 - 整数读写可手动翻转字节序:
htons()/ntohs()用于 16 位,htonl()/ntohl()用于 32 位;64 位需自定义或用bswap_64()(Linux)或_byteswap_uint64()(MSVC)
二进制文件读写看着简单,真正踩坑的地方都在细节:标志位漏写、读取后不验字节数、类型强转不彻底、跨平台假设太乐观。尤其是 gcount() 这个容易被忽略的函数,它才是判断一次读操作是否“真正完成”的唯一可靠依据。










