POD类型结构体才可安全用memcpy序列化,需满足标准布局且无用户定义构造/析构/虚函数;否则会写入padding、指针或未初始化成员,导致读取错误。

用 memcpy 直接写结构体,但必须满足 POD 类型
能直接二进制序列化结构体的前提是:它得是标准布局(standard layout)且无用户定义构造/析构/虚函数的 POD 类型。否则 memcpy 会把 padding 字节、指针地址或未初始化成员一起写进去,读出来就是错的。
- 检查方式:编译期加
static_assert(std::is_pod_v<mystruct>, "not POD");</mystruct> - 常见破环点:
std::string、std::vector、引用成员、非 public 非 static 数据成员混排 - 有
std::array<int></int>没问题,但换成int*就不行——指针值序列化后毫无意义
示例安全结构体:
struct Point {
int x;
int y;
}; // ✅ POD,可 memcpy
写文件时别忽略字节序和对齐差异
结构体在内存里怎么排布,取决于编译器对齐策略和当前平台字节序。跨平台读取时,x86_64 和 aarch64 可能对齐不同;小端机写的文件在大端机上直接读,int32_t 值就全反了。
- 生产环境强烈建议显式控制:用
#pragma pack(1)或alignas(1)消除 padding,再手动处理字段字节序(如用htole32()) - Windows 上默认对齐是 8,Linux GCC 默认也是 8,但若结构体含
__m128成员,可能自动扩展到 16 —— 写出的文件大小会变 - 调试技巧:用
sizeof(MyStruct)和offsetof(MyStruct, field)打印布局,确认和预期一致
遇到非 POD 成员(如 std::string)必须手写序列化逻辑
不能靠 memcpy,必须拆开处理:先写长度,再写内容。否则读的时候根本不知道字符串有多长,更没法分配内存。
立即学习“C++免费学习笔记(深入)”;
- 错误写法:
write(&s, sizeof(s))→ 写的是std::string对象本身(通常 24 字节小对象),不是字符串数据 - 正确顺序:先
size_t len = s.size(); write(&len, sizeof(len));,再write(s.data(), len); - 注意
size_t本身跨平台不一致(32/64 位),建议统一用uint32_t或uint64_t - 如果结构体嵌套多层(比如 vector of struct),递归处理比幻想“一键序列化”更可靠
用 std::ofstream 的 write() 而不是流插入操作符
<< 是格式化输出,会转成文本,完全违背“二进制”目标;而 write() 才是裸字节搬运工。
- 务必打开文件时加
std::ios::binary标志,否则 Windows 下\n会被悄悄替换成\r\n - 写完记得调
flush()或检查good(),否则缓冲区没落盘,程序一退出就丢数据 - 不要用
operator<<写整数然后指望用read()读回来——类型不匹配,字节数对不上
典型安全写法:
std::ofstream f("data.bin", std::ios::binary);
f.write(reinterpret_cast<const char*>(&p), sizeof(p));
f.flush();
结构体二进制序列化的坑不在语法,而在类型属性、内存布局、平台约定三者是否对齐。哪怕一个 std::string 成员没处理,整个结构体就失去二进制可移植性。










