一句话理解 fwrite
「将内存中的二进制‘包裹’批量快递到文件‘仓库’中,按件打包,返回成功投递的包裹数量!」
函数原型
#include // 必须包含头文件
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
入口参数
参数 | 类型 | 比喻解释 |
ptr | const void* | 内存「包裹」的地址(要发送的数据) |
size | size_t | 每个包裹的大小(单位:字节) |
nmemb | size_t | 希望快递的包裹件数 |
stream | FILE* | 文件「仓库」的门(文件指针) |
返回参数
返回值 | 含义 |
size_t | 实际成功快递的包裹件数(≤ nmemb) |
核心功能图解
内存包裹内容:[][][][][][](每个水果占2字节)
调用 fwrite(ptr, 2, 3, file) → 发送3个包裹(每个2字节)
文件仓库内容:[][][][][][]
返回 3(成功发送3件)
代码实例1:保存结构体数组
场景:存档学生成绩
#include
#include
// 定义「包裹」结构
typedef struct {
char name[20];
int score;
} Student;
int main() {
Student classroom[3] = {
{"Alice", 90},
{"Bob", 85},
{"Charlie", 95}
};
FILE *file = fopen("students.dat", "wb"); // 二进制写模式
if (!file) {
perror(" 仓库门打不开");
return 1;
}
// 快递数据:每件包裹=1个Student,发送3件
size_t shipped = fwrite(classroom, sizeof(Student), 3, file);
if (shipped < 3) {
printf(" 只成功发送%zu件包裹,可能仓库爆满了\n", shipped);
} else {
printf(" 3名学生数据已安全存档\n");
}
fclose(file);
return 0;
}
代码实例2:生成图片文件
场景:创建纯色BMP位图
#include
#pragma pack(1) // 禁用内存对齐(确保结构体紧凑存储)
// BMP文件头结构
typedef struct {
char magic[2]; // "BM"
int file_size; // 文件总大小
short reserved[2]; // 保留字段
int data_offset; // 像素数据偏移量
} BMPHeader;
int main() {
BMPHeader header = {
{'B', 'M'}, // 魔数
54 + 640*480*3, // 文件大小=头54字节+RGB数据
{0, 0}, // 保留
54 // 像素数据从54字节开始
};
FILE *image = fopen("red.bmp", "wb");
if (!image) {
perror("创建图片失败");
return 1;
}
// 快递文件头(1个包裹,包裹大小=sizeof(BMPHeader))
if (fwrite(&header, sizeof(BMPHeader), 1, image) != 1) {
printf(" 文件头写入失败\n");
fclose(image);
return 1;
}
// 生成纯红色像素数据(640x480分辨率)
unsigned char red[3] = {0, 0, 255}; // BMP使用BGR格式
for (int i = 0; i < 640*480; i++) {
if (fwrite(red, 3, 1, image) != 1) { // 每个像素3字节
printf(" 像素数据写入失败\n");
break;
}
}
fclose(image);
printf(" 红色BMP图片已生成\n");
return 0;
}
技术细节剖析
1.参数顺序的黄金法则
// 常见错误:size 和 nmemb 写反
fwrite(buffer, 100, 1, file); // 发送1个100字节的包裹
fwrite(buffer, 1, 100, file); // 发送100个1字节的包裹(效果相同,但语义不同)
2.返回值隐藏的密码
返回值 | 含义分析 |
= nmemb | 完美发送所有包裹 |
< nmemb | 可能磁盘空间不足或文件错误 |
0 | 完全发送失败 |
3.二进制模式的必要性
- 在Windows系统中必须用 "wb" 模式,否则可能意外修改数据:
// 错误示范:文本模式写入二进制数据
FILE *file = fopen("data.bin", "w"); // 导致数据损坏(如0x0A被改为0x0D0A)
致命误区
1.内存对齐陷阱
#pragma pack(1) // 必须禁用对齐,否则结构体可能有空隙
typedef struct {
char a;
int b; // 默认对齐后结构体大小为8字节(非5字节)
} CustomStruct;
2.缓冲区溢出
int arr[5] = {1,2,3,4,5};
// 危险!试图写入6个int,但数组只有5个元素
fwrite(arr, sizeof(int), 6, file); // 发送越界数据!
高级技巧:增量写入大文件
#define CHUNK_SIZE 4096 // 每次发送4KB
unsigned char buffer[CHUNK_SIZE];
size_t total_sent = 0;
while (有数据要发送) {
fill_buffer(buffer, CHUNK_SIZE); // 填充数据
size_t sent = fwrite(buffer, 1, CHUNK_SIZE, file);
total_sent += sent;
if (sent < CHUNK_SIZE) {
perror(" 快递中途停止");
break;
}
}
printf(" 总发送量:%zu 字节\n", total_sent);
对比 fread
操作 | 方向 | 常见用途 |
fwrite | 内存→文件 | 保存数据、序列化 |
fread | 文件→内存 | 加载数据、反序列化 |
总结
- 核心功能:批量写入二进制数据到文件
- 必用场景:保存游戏存档、生成图像/音频、日志记录
- 类比记忆:就像用传送带把工厂流水线上的标准货箱批量装船,fwrite 是C语言输出二进制数据的「物流指挥官」