指针不能直接序列化,必须解引用后按类型安全处理:POD对象可memcpy,数组需记录长度,复杂类型须调用其序列化逻辑;注意对齐、字节序、浮点特殊值及跨平台兼容性,推荐使用std::span和Protocol Buffers等现代方案。

指针本身不能直接序列化,必须先解引用或转换为可序列化数据
直接对 int*、char* 这类裸指针调用 write() 或 memcpy 到缓冲区,只会保存指针地址值(如 0x7fffa1234567),而非它指向的内容。这在反序列化时必然失效——目标进程该地址大概率无效或被占用。
真正可行的路径是:明确知道指针指向的是什么类型、多大尺寸、是否含嵌套结构,然后按需处理:
- 若指向 POD 类型单个对象(如
struct Point { int x, y; };),可安全memcpy(&buf[off], ptr, sizeof(*ptr)) - 若指向定长数组(如
float* data = new float[1024];),需同时记录长度,并序列化data指向的 1024 个float值,而非data本身 - 若指向
std::string、std::vector或含虚函数/指针成员的类,则不能 memcpy,必须调用其序列化逻辑(如str.size()+str.data()分两段写)
使用 reinterpret_cast<char></char> 读写内存块前,必须确认对齐与字节序
将 double* p 强转为 char* 写入文件,看似简单,但实际有隐藏陷阱:
-
sizeof(double)是 8,但若结构体中有char a; double b;,&b可能不是 8 字节对齐地址,reinterpret_cast后 memcpy 可能触发未定义行为(尤其在 ARM 或严格对齐平台) - 跨平台传输时,发送端是小端 x86_64,接收端是大端 PowerPC,直接写
uint32_t值会错乱——必须用htons()/htonl()或手动翻转字节 - 浮点数二进制表示(IEEE 754)虽通用,但某些嵌入式平台不支持 denormal 数,序列化前应检查
std::fpclassify(*p)
避免序列化指向堆内存的指针,优先用值语义或智能指针管理生命周期
常见错误模式:MyClass* obj = new MyClass(); serialize(&obj); —— 这里 &obj 是栈上指针变量的地址,序列化它毫无意义;而 obj 本身是堆地址,不能复原。
立即学习“C++免费学习笔记(深入)”;
更健壮的做法是:
- 让类自己提供
serialize(std::vector<char>& buf)</char>成员函数,内部处理所有字段(含std::unique_ptr所指对象) - 若必须传递裸指针参数,函数签名应为
void serialize(const T* ptr, size_t count),并强制要求调用方保证ptr非空、count > 0、内存有效 - 对多态对象,不要传
Base*然后只序列化Base部分——必须显式调用dynamic_cast或通过虚函数get_type_id()记录类型信息,再分发序列化逻辑
用 std::span<const std::byte></const> 替代裸指针做序列化输入更安全
C++20 起,std::span 能明确表达“一段连续、已知长度的原始字节”,比 const void* + size_t 更不易出错:
void write_blob(std::span<const std::byte> data) {
// 不再需要额外传 length 参数,也不怕传错 size
file.write(reinterpret_cast<const char*>(data.data()), data.size());
}
配合 std::bit_cast(C++20)或 std::memcpy,可安全地把结构体转成字节块:
Point p{1, 2};
auto bytes = std::as_bytes(std::span(&p, 1));
write_blob(bytes);
注意:std::as_bytes 要求 T 是 trivially_copyable,且不适用于含 padding 或非标准布局的类。
最易被忽略的一点:序列化后的二进制数据没有自描述性。哪怕你正确保存了指针所指内容,反序列化时仍需完全相同的内存布局、编译器 ABI、甚至相同的 sizeof(long) —— 否则 read() 出来的值就是垃圾。生产环境建议用 Protocol Buffers 或 flatbuffers,而不是手撸指针序列化。


















