跳转到主内容
websoft网络软件专家 - 深耕网络技术,打造实用软件!

C++实现结构体到二进制文件序列化 _ 指针强转与字节流读写【实战】

仅POD类型结构体可安全用reinterpret_cast写文件;含指针、string、vector或虚函数的非POD类型会因野指针或无效数据导致崩溃。 直接用
reinterpret_cast
强转结构体指针写文件,多数情况会出错 结构体里只要含指针、
std::string
、
std::vector
或虚函数表(比如有虚函数),就不能简单把整个对象地址 reinterpret_cast 成
char*
写进文件。因为这些成员存的是运行时堆地址或内部动态数据,序列化后读出来只会得到野指针或无效长度,运行时大概率崩溃。 真正能安全强转的,仅限于「POD 类型」:纯 C 风格结构体,所有成员都是内置类型(
int
、
double
、
char[16]
等),无构造函数、析构函数、虚函数、非静态成员函数、访问控制符(
private
/
protected
)——且编译器没加 padding 干扰(需确认内存布局)。 检查是否 POD:
std::is_pod_v
(C++17 起已弃用但仍可用),更推荐
std::is_trivially_copyable_v
+
std::is_standard_layout_v
强制对齐避免 padding:
#pragma pack(1)
或
alignas(1)
,但注意跨平台读写时 字节 序和对齐差异 写入前务必用
sizeof(MyStruct)
校验大小,别信结构体声明看起来多“紧凑” 用
std::ofstream.write()
写二进制,必须配合
ios::binary
标志 漏掉
ios::binary
是 Windows 下最常见翻车点:文本模式会把
\n
自动转成
\r\n
,导致读取时字节偏移全乱,结构体字段错位。Linux/macOS 虽不转换换行符,但显式指定 binary 更安全、语义更清晰。 正确打开方式:
std::ofstream file("data.bin", std::ios::binary);
写结构体(仅限 trivially copyable):
file.write(reinterpret_cast(&s), sizeof(s));
写多个连续结构体(如数组):
file.write(reinterpret_cast(arr), sizeof(MyStruct) * count);
写完记得
file.close()
或作用域结束自动析构,否则缓冲区可能未刷盘 读取时不能只靠
read()
,还要校验长度与 EOF 用
std::ifstream.read()
读二进制,返回值是流对象本身,不能直接判断成功与否;实际要查
gcount()
返回值是否等于预期字节数,否则可能只读了一半就遇到文件尾或 I/O 错误。 C函数速查手册(CHM版) C函数速查手册(CHM版) 下载 立即学习 “ C++免费学习笔记(深入) ”; 读单个结构体示例:
MyStruct s; in.read(reinterpret_cast(&s), sizeof(s)); if (in.gcount() != sizeof(s)) { // 读取失败:文件损坏、提前 EOF 或权限问题 }
读数组时循环内每次都要检查
gcount()
,别假设
read()
一定能读满 结构体含固定长度数组(如
char name[32]
)时,读出后建议用
strnlen
截断,避免后续当 C 字符串用时越界 跨平台序列化必须处理字节序(Endianness) x86/x64 默认小端,ARM 可能大端,网络协议常用大端。如果文件要在不同架构机器间交换,整数字段(
int32_t
、
uint64_t
等)必须统一字节序,否则读出来数值完全错误。 写入前转网络字节序:
htons()
(16 位)、
htonl()
(32 位)、
htobe64()
(POSIX)或手动移位 读取后转主机字节序:
ntohs()
、
ntohl()
、
be64toh()
浮点数没有标准字节序转换函数,需按整数 reinterpret_cast 后再转,或改用文本/Protocol Buffers 等方案 结构体中混用大小端字段极难维护,建议整个文件统一约定(如全用大端),并在文件头加 magic number 和版本标识 结构体序列化的坑不在语法,而在内存模型、I/O 行为和平台差异的叠加。哪怕一个
std::string
成员没手动序列化,或一次
gcount()
检查被跳过,都可能让程序在某个环境静默失败。

相关文章