只有pod结构体才能安全用fwrite写入二进制文件,需满足无虚函数、无非pod成员、无用户定义构造/析构/赋值、无引用或std::string等;否则须手动序列化,且须注意字节对齐与跨平台字节序。

直接用 fwrite 写结构体有风险,先确认是否满足 POD 类型
不是所有 C++ 结构体都能安全用 fwrite 直接写入二进制文件。只有满足 POD(Plain Old Data)要求的结构体才可位拷贝——即不能含虚函数、非 POD 成员、用户定义构造/析构/赋值、引用、std::string 等。
常见错误现象:fwrite 看似成功,但读回后字段错乱、字符串变空、指针地址被写入导致崩溃。
- 检查方式:编译期加
static_assert(std::is_pod_v<mystruct>, "not POD");</mystruct> - 若含
std::string或std::vector,必须手动序列化其内容,不能整体fwrite - 注意字节对齐:不同编译器或
#pragma pack设置会导致内存布局不一致,跨平台存取前需统一#pragma pack(1)
使用 fwrite 写入 POD 结构体的正确姿势
核心是确保传给 fwrite 的地址指向**连续、有效、生命周期未结束**的内存块。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 用
&s取地址,而非s(结构体变量名不是指针) - 第三个参数固定为
1,第四个参数用sizeof(s),避免误写成sizeof(&s) - 务必检查返回值:
fwrite返回实际写入的项数(应为 1),不为 1 则需用ferror()或feof()判断原因
MyStruct s = {123, 45.6f, 'X'};
FILE* fp = fopen("data.bin", "wb");
if (fp) {
size_t written = fwrite(&s, sizeof(s), 1, fp);
if (written != 1) {
// 处理写入失败
}
fclose(fp);
}
读取时 fread 必须与写入严格对齐
读取不是“反向操作”那么简单——它依赖写入时的内存布局、字节序、对齐方式完全一致。哪怕结构体只改了一个字段顺序,旧文件就可能读错。
- 读取前建议先
fseek(fp, 0, SEEK_END); long size = ftell(fp); fseek(fp, 0, SEEK_SET);校验文件大小是否等于sizeof(MyStruct) - 用
fread(&s, sizeof(s), 1, fp),和fwrite参数一一对应 - 若文件含多个对象,循环读取时注意不要越界;用
fread返回值判断是否读完(返回 0 表示 EOF 或错误) - 整数字段若涉及跨平台(如 Windows ↔ Linux),需自行处理字节序(
htons/ntohl),fwrite不做转换
遇到非 POD 成员(如 std::string)怎么办
不能跳过,也不能直接写指针。必须拆解:先写长度,再写字符数据。
- 写入示例:
size_t len = s.name.length(); fwrite(&len, sizeof(len), 1, fp); fwrite(s.name.c_str(), 1, len, fp); - 读取时先读
len,再动态分配缓冲区或用std::string::resize预留空间,再fread填充 - 若结构体含指针成员(即使你没主动写),说明它根本不是 POD,
fwrite会把指针值(地址)写入,读回后必然失效 - 这种场景建议改用更健壮的序列化方案(如
protobuf、msgpack),或至少封装成save()/load()成员函数
真正麻烦的从来不是 fwrite 这一行代码,而是结构体定义是否稳定、跨平台字节序是否一致、以及非 POD 成员是否被悄悄引入——这些细节一旦出错,二进制文件就变成无法解析的黑盒。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










