在C++开发里,将多个结构体按顺序写入同一个二进制文件是常见的数据持久化需求,比如存储日志、配置信息或者自定义数据格式时都会用到这个操作。核心思路是利用二进制文件的连续存储特性,逐个将结构体的内存数据写入文件,配合块写入函数可以提升操作效率。

核心实现原理
二进制文件写入不需要像文本文件那样做格式转换,直接操作内存中的原始字节数据。C++中可以使用fwrite函数实现块写入,它的作用是将一段连续内存区域的数据一次性写入文件,非常适合结构体这种连续内存的数据类型。
需要注意结构体可能存在内存对齐的问题,不同编译器默认的对齐规则可能不同,如果写入的结构体需要跨平台读取,建议显式关闭内存对齐,避免不同环境下读取的数据出现偏差。
基础实现步骤
1. 定义结构体
首先定义需要写入的结构体,这里以存储用户信息的结构体为例:
#include <stdio.h>
#include <string.h>
// 关闭内存对齐,保证结构体占用的字节数是成员大小之和
#pragma pack(push, 1)
struct UserInfo {
int id;
char name[20];
float score;
};
#pragma pack(pop)
2. 打开二进制文件
使用fopen函数以二进制写入模式打开文件,模式字符串为"wb",如果文件不存在会自动创建,存在则会清空原有内容:
FILE* file = fopen("user_data.bin", "wb");
if (file == NULL) {
printf("文件打开失败n");
return -1;
}
3. 按顺序写入多个结构体
创建多个结构体实例,然后逐个使用fwrite写入文件。fwrite的参数依次是:数据起始地址、每个数据块的字节数、数据块数量、文件指针。写入结构体的时候,每个数据块大小就是结构体的sizeof值,数据块数量为1:
// 创建三个用户结构体实例 struct UserInfo user1; user1.id = 1; strcpy(user1.name, "张三"); user1.score = 89.5f; struct UserInfo user2; user2.id = 2; strcpy(user2.name, "李四"); user2.score = 92.0f; struct UserInfo user3; user3.id = 3; strcpy(user3.name, "王五"); user3.score = 85.5f; // 逐个写入结构体 fwrite(&user1, sizeof(struct UserInfo), 1, file); fwrite(&user2, sizeof(struct UserInfo), 1, file); fwrite(&user3, sizeof(struct UserInfo), 1, file);
4. 关闭文件
写入完成后需要关闭文件,刷新缓冲区确保数据全部写入磁盘:
fclose(file);
批量块写入优化
如果有多个同类型的结构体存放在数组中,可以一次性批量写入,减少函数调用次数,提升写入效率:
// 定义结构体数组
struct UserInfo users[3];
users[0].id = 1;
strcpy(users[0].name, "张三");
users[0].score = 89.5f;
users[1].id = 2;
strcpy(users[1].name, "李四");
users[1].score = 92.0f;
users[2].id = 3;
strcpy(users[2].name, "王五");
users[2].score = 85.5f;
FILE* file = fopen("user_data.bin", "wb");
if (file == NULL) {
printf("文件打开失败n");
return -1;
}
// 一次性写入整个数组,数据块数量为3
fwrite(users, sizeof(struct UserInfo), 3, file);
fclose(file);
常见问题说明
- 内存对齐问题:如果没有关闭内存对齐,结构体实际占用的字节数会大于成员大小之和,写入的字节数会包含填充的空字节,跨平台读取时容易出现数据错位,建议写入前用
#pragma pack调整对齐规则。 - 字符串写入问题:结构体中的字符数组如果存储的是短字符串,剩余部分会用空字符填充,写入后读取时不影响使用,但如果是指针类型的字符串成员,写入的只是指针地址,没有实际字符串内容,这种结构体不适合直接二进制写入。
- 写入完整性检查:
fwrite的返回值是实际成功写入的数据块数量,写入后可以判断返回值是否和预期的数量一致,避免写入不完整:
size_t write_count = fwrite(&user1, sizeof(struct UserInfo), 1, file);
if (write_count != 1) {
printf("数据写入失败n");
}
读取验证
写入完成后可以编写读取代码验证数据是否正确,读取的时候按照写入的顺序,逐个读取同样大小的结构体即可:
FILE* read_file = fopen("user_data.bin", "rb");
if (read_file == NULL) {
printf("文件打开失败n");
return -1;
}
struct UserInfo read_user;
while (fread(&read_user, sizeof(struct UserInfo), 1, read_file) == 1) {
printf("id: %d, name: %s, score: %.1fn", read_user.id, read_user.name, read_user.score);
}
fclose(read_file);