会出问题,因结构体存在填充字节、指针或非POD成员(如std::string)导致memcpy仅复制地址而非实际数据;仅当结构体为标准布局且平凡可拷贝、无指针虚函数、成员全为固定大小POD类型并禁用对齐优化时才安全。
会,而且非常容易出问题。核心原因是 struct 在内存中可能包含填充字节(padding),而这些字节的值是未定义的;如果结构体里有指针、std::string、std::vector 等非 POD 类型,直接 memcpy 只会拷贝指针地址,不是实际数据。
只有满足以下全部条件时,才能安全地用 memcpy + write() 直接二进制导出:
struct 是标准布局(standard layout)且是平凡类型(trivially copyable)int、float、char[32])#pragma pack(1) 对齐)例如这个结构体可以安全序列化:
struct Header {
uint32_t magic;
uint16_t version;
char name[16];
} __attribute__((packed)); // GCC / Clang;MSVC 用 #pragma pack(1)
不能跳过序列化逻辑——必须把 std::string 的长度和内容分开写入。关键点是:先写长度(size_t 或固定宽度如 uint32_t),再写字符数据,读取时按同样顺序反向操作。
示例结构体及序列化函数:
struct Person {
int id;
std::string name;
double salary;
};
void serialize(const Person& p, std::ofstream& out) {
out.write(reinterpret_cast(&p.id), sizeof(p.id));
uint32_t len = static_cast(p.name.size());
out.write(reinterpret_cast(&len), sizeof(len));
if (len > 0) {
out.write(p.name.c_str(), len);
}
out.write(reinterpret_cast(&p.salary), sizeof(p.salary));
}
注意: 注意:std::string::c_str() 不保证以 结尾写入(我们只写 std::string::c_str() 不保证以 \0 结尾写入(我们只写 len 字节),读取时也需用 std::string::assign(const char*, size_t) 构造。len 字节),读取时也需用 std::string::assign(const char*, size_t) 构造。
主要在三处:字节序(endianness)、整数宽度、浮点格式。比如 sizeof(size_t) 在 32 位和 64 位系统不同;double 虽然通常是 IEEE 754,但某些嵌入式平台不保证。
实战建议:
uint32_t 替代 unsigned int,int64_t 替代 long long
htons/htonl/htobe64 转换(Linux/macOS 支持 be64toh,Windows 需自己实现)time_t、off_t 等平台相关类型有,但别碰 Boost.Serialization —— 它重度依赖 RTTI 和模板膨胀,编译慢、二进制大、难以调试。更实用的选择是:
std::string 和容器开箱即用,无运行时依赖std::bit_cast(C++20):对纯 POD 类型,可用 std::bit_cast<:array n>>(obj) 安全转为字节数组,比 reinterpret_cast 更语义清晰如果只是临时存配置或调试快照,用 cereal 最省心;如果追求极致性能且能接受预定义 schema,FlatBuffers 值得投入学习成本。
真正难的从来不是“怎么写进去”,而是“下次读出来时,字段变了、平台换了、编译器升级了,还能不能认出原来的数据”。多留一个版本字段,比事后翻日志查字节偏移强十倍。