直接用 fwrite 写结构体易因字节对齐导致读取错位,应逐字段写入并检查 fread 返回值,二进制文件必须用 "wb"/"rb" 模式打开。

fwrite 写入结构体时字节对齐会导致读取错位
直接用 fwrite 写结构体看似简单,但结构体成员默认按编译器对齐规则填充空白字节。如果写入端和读取端的结构体定义或编译选项(如 #pragma pack)不一致,fread 会把填充字节误读为有效数据,导致字段值全乱。
- 写入前确认结构体实际大小:用
sizeof(MyStruct)打印,别只看成员总和 - 强制取消对齐(推荐):
#pragma pack(1) struct Data { int id; double value; char name[32]; }; #pragma pack() - 更安全的做法是逐字段写入,避开结构体内存布局问题
fread 返回值不等于请求字节数就说明出错了
fread 的返回值是「成功读取的元素个数」,不是字节数。若以 sizeof(struct) 为 size、1 为 count 调用,返回 1 才算完整读取;返回 0 不一定代表文件结束,可能是读取失败(比如磁盘权限不足或文件被截断)。
- 必须检查
ferror(fp)和feof(fp)区分错误类型 - 不要用
while (!feof(fp))循环读取——这是经典陷阱,会导致最后一次读取重复处理 - 正确模式:
while (fread(&data, sizeof(data), 1, fp) == 1) { // 处理 data }
二进制文件必须用 "wb" 和 "rb" 模式打开
在 Windows 上,文本模式("w"/"r")会把 \n 自动转成 \r\n,破坏原始字节流;Linux 虽然影响小,但跨平台代码必须统一用二进制模式。
-
fopen("data.bin", "wb")写入前清空文件并禁用换行转换 -
fopen("data.bin", "rb")读取时不解释任何字节为控制字符 - 用
"ab"追加写入时也要配"rb"读取,否则可能读到旧数据末尾的垃圾字节
完整可运行示例:写入/读取结构体数组
下面是一个不依赖对齐、显式控制字节流的最小可行版本,适用于 C++ 编译器(g++ / clang++ / MSVC):
立即学习“C++免费学习笔记(深入)”;
#include#include struct Record { int id; float score; char tag[8]; };
int main() { // 写入 FILE* fp = fopen("records.dat", "wb"); if (!fp) { std::cerr << "无法写入文件\n"; return 1; }
Record arr[] = {{101, 95.5f, "A"}, {202, 87.0f, "B"}}; for (int i = 0; i zuojiankuohaophpcn 2; ++i) { fwrite(&arr[i].id, sizeof(int), 1, fp); fwrite(&arr[i].score, sizeof(float), 1, fp); fwrite(arr[i].tag, sizeof(arr[i].tag), 1, fp); } fclose(fp); // 读取 fp = fopen("records.dat", "rb"); if (!fp) { std::cerr zuojiankuohaophpcnzuojiankuohaophpcn "无法读取文件\n"; return 1; } Record r; while (fread(&r.id, sizeof(int), 1, fp) == 1 && fread(&r.score, sizeof(float), 1, fp) == 1 && fread(r.tag, sizeof(r.tag), 1, fp) == 1) { std::cout zuojiankuohaophpcnzuojiankuohaophpcn "id=" zuojiankuohaophpcnzuojiankuohaophpcn r.id zuojiankuohaophpcnzuojiankuohaophpcn ", score=" zuojiankuohaophpcnzuojiankuohaophpcn r.score zuojiankuohaophpcnzuojiankuohaophpcn ", tag=" zuojiankuohaophpcnzuojiankuohaophpcn r.tag zuojiankuohaophpcnzuojiankuohaophpcn "\n"; } if (ferror(fp)) std::cerr zuojiankuohaophpcnzuojiankuohaophpcn "读取发生 I/O 错误\n"; fclose(fp); return 0;}
关键点在于:每个字段单独
fwrite/fread,完全绕开结构体内存布局;每步都检查返回值;关闭文件前确保无未刷新缓冲区(fclose自动 flush)。真正麻烦的从来不是调用函数本身,而是对齐、错误码、模式字符串这三个地方漏掉任意一个,整个二进制读写就会静默失败。









