直接用 fwrite 写结构体易因字节对齐导致读取错位,应逐字段写入并检查 fread 返回值,二进制文件必须用 "wb"/"rb" 模式打开。
直接用 fwrite 写结构体看似简单,但结构体成员默认按编译器对齐规则填充空白字节。如果写入端和读取端的结构体定义或编译选项(如 #pragma pack)不一致,fread 会把填充字节误读为有效数据,导致字段值全乱。
sizeof(MyStruct) 打印,别只看成员总和#pragma pack(1)
struct Data {
int id;
double value;
char name[32];
};
#pragma pack()
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):
#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)。真正麻烦的从来不是调用函数本身,而是对齐、错误码、模式字符串这三个地方漏掉任意一个,整个二进制读写就会静默失败。