C语言fwrite函数详解:“数据快递员”

一句话理解 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语言输出二进制数据的「物流指挥官」