C 语言二进制读写
更新时间:2026-08-26。本文是
languages/c/主题入门层第 64 篇。前面读写的都是文本——人类看得懂,但解析慢、体积大。要是数据量大(几十万条记录)、或要原样保存结构体,就该上二进制:fwrite把内存字节直接写盘,fread原样读回。快、省空间,但换平台就翻车的坑也埋在这里。
本文要回答的问题
fwrite/fread怎么用?参数什么意思?- 结构体能不能直接整个写进去?
- 二进制格式有什么代价?
一、fwrite:把内存原样写盘
#include <stdio.h>
int arr[1000];
/* ... 填充 arr ... */
FILE *fp = fopen("data.bin", "wb"); // 二进制模式!
size_t written = fwrite(arr, sizeof(int), 1000, fp);
fclose(fp);参数四个:数据地址、单个元素大小、元素个数、FILE*。返回值是成功写入的元素个数,应该等于第三个参数。
fwrite(arr, sizeof(int), 1000, fp) 读作:把 arr 里 1000 个 int(每个 sizeof(int) 字节)连续写盘。
二、fread:原样读回
int buf[1000];
FILE *fp = fopen("data.bin", "rb");
size_t n = fread(buf, sizeof(int), 1000, fp);
fclose(fp);
// n = 实际读到的元素个数(文件不足时 < 1000)读写对称:写几个元素,读就请求几个元素。返回值 n 是实际读到的个数——文件比预期短时 n 会小于请求数,用 n 而非固定值遍历。
三、结构体直接落盘
二进制最大的便利:结构体整体写、整体读:
typedef struct { int id; char name[32]; double score; } Student;
Student s = {1001, "alice", 95.5};
FILE *fp = fopen("stu.bin", "wb");
fwrite(&s, sizeof(Student), 1, fp); // 整个结构体一次写入
fclose(fp);
// 读回
Student t;
fp = fopen("stu.bin", "rb");
fread(&t, sizeof(Student), 1, fp);
fclose(fp);
printf("%d %s %.2f\n", t.id, t.name, t.score);一条记录一个结构体,多条的数组同理:fwrite(students, sizeof(Student), n, fp)。
注意:结构体里的指针成员(如 char *name)不能直接落盘——写进去的是地址值,读回来地址早失效了。指针指向的内容要单独处理(或改用定长数组)。
四、文本 vs 二进制
| 对比项 | 文本 | 二进制 |
|---|---|---|
| 可读性 | 人类可读 | 乱码 |
| 体积 | 大("123456" 6 字节) | 小(int 4 字节) |
| 读写速度 | 慢(格式化解析) | 快(字节拷贝) |
| 精度 | 浮点转文本有舍入 | 原样保存 |
| 可移植性 | 好(标准字符) | 差(字节序/大小) |
| 易调试 | 好 | 难 |
选型:配置、日志、人要看的数据 → 文本;大规模数值数据、存档、程序间紧密耦合 → 二进制。
五、二进制的可移植性坑
二进制文件换机器可能读不了:
- 字节序(endian):x86 小端,网络/大端机器(SPARC 等)反着存;
- 类型大小:
sizeof(int)在不同平台可能是 4 或 2; - 结构体对齐填充:不同编译器的 padding 不同,
sizeof(Student)可能不同; - 浮点格式:IEEE 754 基本通用,但极端平台例外。
对策:文件只在同类平台间交换(最常见);跨平台就规定字节序(如小端 + 定长类型 int32_t),见大端小端与字节序。
六、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
| 文本模式开二进制 | \r\n 污染数据 | 用 "wb"/"rb" |
| 指针成员落盘 | 读回悬垂地址 | 指针内容单独处理 |
| 不检查 fread 返回值 | 数据不足还用 | 用 n 遍历 |
| 跨平台交换二进制 | 数据全错 | 固定字节序/类型 |
| 误以为二进制更小就好 | 可读性/可移植牺牲 | 按需选型 |
七、与本站主线衔接
- 文本格式的读写与解析,见格式化读写 fscanf/fprintf;
- 字节序与大小端的深入,见大端小端与字节序;
- 结构体内存布局与对齐,见结构体内存对齐。
一句话总结
fwrite/fread 把内存字节原样写/读盘(地址、元素大小、个数、FILE*),结构体可整体落盘、指针成员除外;二进制快、小、保精度,但受字节序/类型大小/对齐影响——跨平台就规定格式,否则只在同类平台间交换——二进制是"快递原封不动",快但挑收件人。