一句话理解 fread
「从文件的‘仓库’中批量搬运二进制‘货物’到内存‘卡车’中,按需装货,返回实际搬运的货物数量!」
函数原型
c
复制
#include // 必须包含头文件
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
入口参数
参数 | 类型 | 比喻解释 |
ptr | void* | 内存「卡车」的地址(存放数据) |
size | size_t | 每件「货物」的大小(单位:字节) |
nmemb | size_t | 希望搬运的货物件数 |
stream | FILE* | 文件「仓库」的门(文件指针) |
返回参数
返回值 | 含义 |
size_t | 实际成功搬运的货物件数(≤ nmemb) |
核心功能图解
假设文件内容(二进制):[A][A][B][B][C][C](每个字母占2字节)
调用 fread(ptr, 2, 3, file) → 搬运3件货物(每件2字节)
内存卡车内容:[A][A][B][B][C][C]
返回 3(成功搬运3件)
代码实例1:读取结构体数组
场景:读取学生成绩存档
#include
#include
// 定义「货物」结构
typedef struct {
char name[20];
int score;
} Student;
int main() {
FILE *file = fopen("students.bin", "rb"); // 二进制读模式
if (!file) {
perror(" 仓库门打不开");
return 1;
}
Student classroom[3]; // 准备3个学生的「卡车」
// 从文件搬运数据:每件货物=1个Student,最多搬3件
size_t loaded = fread(classroom, sizeof(Student), 3, file);
if (loaded < 3) {
printf(" 只搬运了%zu件货物,可能仓库没货了\n", loaded);
} else {
printf(" 成功搬运3名学生数据\n");
for (int i = 0; i < 3; i++) {
printf("学生%d: %s, 分数=%d\n",
i+1, classroom[i].name, classroom[i].score);
}
}
fclose(file);
return 0;
}
代码实例2:读取图片文件头
场景:解析PNG文件特征
#include
// PNG文件头特征(固定8字节)
#define PNG_HEADER_SIZE 8
int main() {
FILE *image = fopen("photo.png", "rb");
if (!image) {
perror("图片仓库失联");
return 1;
}
unsigned char header[PNG_HEADER_SIZE]; // 准备8字节的「小推车」
// 搬运文件头:每件1字节,搬8件
if (fread(header, 1, PNG_HEADER_SIZE, image) != PNG_HEADER_SIZE) {
printf(" 文件头不完整\n");
fclose(image);
return 1;
}
// 检查PNG特征(89 50 4E 47 0D 0A 1A 0A)
if (header[0] == 0x89 && header[1] == 0x50 &&
header[2] == 0x4E && header[3] == 0x47) {
printf(" 这是合法的PNG图片\n");
} else {
printf(" 文件头异常,疑似损坏\n");
}
fclose(image);
return 0;
}
技术细节剖析
1.参数顺序的黄金法则
// 常见错误:size 和 nmemb 写反
fread(buffer, 100, 1, file); // 搬运1件100字节的货物
fread(buffer, 1, 100, file); // 搬运100件1字节的货物(效果相同,但语义不同)
2.返回值隐藏的密码
返回值 | 含义分析 |
= nmemb | 完美搬运所有货物 |
< nmemb | 可能遇到文件尾(EOF)或错误 |
0 | 仓库已空或发生错误 |
3.二进制模式的必要性
- 在Windows系统中,必须用 "rb" 模式打开文件,否则会遇到换行符转换问题:
// 错误示范:文本模式读取二进制文件 FILE *file = fopen("data.bin", "r"); // 导致数据损坏
致命误区
1.缓冲区溢出
int arr[5];
// 危险!试图读取6个int,但卡车只能装5个
fread(arr, sizeof(int), 6, file); // 内存越界!
2.类型不匹配
float data[10];
// 错误!文件存储的是double类型
fread(data, sizeof(double), 10, file); // 数据解析错误
高级技巧:分块读取大文件
#define CHUNK_SIZE 4096 // 4KB的「卡车容量」
unsigned char buffer[CHUNK_SIZE];
size_t total_read = 0;
while (1) {
size_t read = fread(buffer, 1, CHUNK_SIZE, file);
total_read += read;
if (read < CHUNK_SIZE) {
if (feof(file)) {
printf(" 全部货物搬运完成,总量:%zu字节\n", total_read);
} else if (ferror(file)) {
perror("搬运事故");
}
break;
}
// 处理本批次数据...
}
对比 fwrite
操作 | 方向 | 常见用途 |
fread | 文件→内存 | 加载数据、反序列化 |
fwrite | 内存→文件 | 保存数据、序列化 |
总结
- 核心功能:批量读取二进制数据到内存
- 必用场景:加载游戏存档、处理图像/音频、网络数据传输
- 类比记忆:就像用叉车从仓库中成批搬运标准规格的集装箱到卡车上,fread 是C语言处理二进制数据的「物流专家」