pod类型可直接二进制读写,但需注意字节对齐、大小端及跨平台一致性;含stl成员的类须手动序列化字段;自定义格式应使用固定宽度整型、规避浮点位表示差异并添加校验。

用 write() 和 read() 直接读写 POD 类型对象
如果类是纯 POD(比如只有 int、double、数组,无虚函数、无非 trivial 构造/析构、无引用/指针成员),最简方式就是把整个对象当二进制块 dump 到文件:
struct Point {
double x, y;
};
Point p{1.5, -2.3};
std::ofstream ofs("point.bin", std::ios::binary);
ofs.write(reinterpret_cast<const char*>(&p), sizeof(p));
反序列化同理:read() 进内存再 reinterpret_cast。但注意:
- 必须确保编译器没做 padding 优化(加
#pragma pack(1)或用[[gnu::packed]]) - 跨平台时大小端不一致会出错(比如 x86 小端写入,ARM 大端读取)
- 不同编译器或标准库版本下
sizeof(std::string)不固定,含 STL 成员的类不能这么干
含 std::string 或容器的类怎么二进制存?
不能直接 write() 整个对象,因为 std::string 内部是指针,只存了地址,序列化后读出来是野指针。
必须手动拆解字段,分步写入:
立即学习“C++免费学习笔记(深入)”;
struct Person {
std::string name;
int age;
std::vector<double> scores;
};
<p>std::ofstream ofs("person.bin", std::ios::binary);
size_t len = person.name.size();
ofs.write(reinterpret_cast<const char<em>>(&len), sizeof(len)); // 先写长度
ofs.write(person.name.data(), len); // 再写内容
ofs.write(reinterpret_cast<const char</em>>(&person.age), sizeof(person.age));
size_t sz = person.scores.size();
ofs.write(reinterpret_cast<const char<em>>(&sz), sizeof(sz));
if (sz > 0) {
ofs.write(reinterpret_cast<const char</em>>(person.scores.data()), sz * sizeof(double));
}
反序列化时顺序严格对应:先读长度,再按长度分配空间并读内容。漏一步或顺序错,后续全乱。
为什么不用 boost::serialization?它真能省事吗?
能省事,但代价明显:
- 引入外部依赖,编译变慢,二进制体积增大(哪怕只用一个
serialize()函数) - 默认文本格式(XML/JSON)不是二进制;要二进制得显式用
boost::archive::binary_oarchive - 需为每个类定义
serialize()成员函数或友元,且所有成员必须可序列化(比如裸指针不行,std::unique_ptr需特化) - 版本兼容性差:类字段增删后,旧文件读入新程序可能崩溃,得手动加版本号逻辑
适合原型快验或内部工具;生产环境对体积/启动时间敏感时,慎选。
自定义二进制格式要注意哪些隐性坑?
自己设计格式最容易栽在三处:
-
sizeof(size_t)在 32/64 位系统上不同 → 存长度字段务必用固定宽类型,如uint32_t或uint64_t - 浮点数直接
write()虽可行,但NaN、Inf的 bit 表示跨平台未必一致;金融/科学计算建议转成字符串或整数倍单位存储 - 没有校验机制:单字节损坏会导致后续全部解析错位 → 至少加个简单 CRC32 或文件末尾写总长度校验值
真正难的不是“怎么写进去”,而是“多年后换机器、换编译器、换操作系统,还能准确读出来”。每加一个字段,都要想清楚它未来能不能删、能不能改类型、要不要向后兼容。











