如何在C语言中读写二进制文件中的两种不同结构体?
从单个二进制文件读取不同C结构体的可行方案及实现
当然可以!在C语言里实现单个二进制文件存储/读取多种结构体完全没问题,核心是要给每个结构体数据加上“类型标识”,让程序读取时能判断该用哪种结构体去解析字节流。我在做本地数据持久化工具时经常用这种方案,下面给你详细讲实现步骤和注意事项:
核心思路:带类型标记的序列化/反序列化
最通用的做法是在写入每个结构体数据前,先写入一个类型枚举值,告诉后续读取程序“接下来的字节属于哪种结构体”。读取时先读这个标记,再根据标记选择对应的结构体进行数据读取。
步骤1:定义类型枚举和结构体
先定义你要存储的结构体,以及对应的类型标记:
#include <stdio.h> #include <stdlib.h> #include <stdint.h> // 用固定宽度类型避免字节数问题 // 定义结构体类型标记,用枚举保证可读性 typedef enum { TYPE_USER, TYPE_PRODUCT, TYPE_MAX // 用于边界检查 } StructType; // 示例结构体1:用户信息 typedef struct { uint32_t id; char name[32]; uint8_t age; } User; // 示例结构体2:产品信息 typedef struct { uint32_t product_id; float price; uint16_t stock; } Product;
步骤2:写入二进制文件的实现
写入时,先写类型标记,再写对应的结构体内容:
// 写入单个结构体到文件 int write_struct(FILE *fp, StructType type, const void *data) { // 先写入类型标记(用fwrite保证二进制写入) if (fwrite(&type, sizeof(StructType), 1, fp) != 1) { perror("Failed to write struct type"); return -1; } // 根据类型写入对应的结构体数据 switch(type) { case TYPE_USER: if (fwrite(data, sizeof(User), 1, fp) != 1) { perror("Failed to write User"); return -1; } break; case TYPE_PRODUCT: if (fwrite(data, sizeof(Product), 1, fp) != 1) { perror("Failed to write Product"); return -1; } break; default: fprintf(stderr, "Unknown struct type\n"); return -1; } return 0; } // 示例:写入几个测试数据 int main() { FILE *fp = fopen("data.bin", "wb"); if (!fp) { perror("Failed to open file"); return 1; } User user1 = {1001, "Alice", 28}; Product prod1 = {2001, 99.9f, 50}; User user2 = {1002, "Bob", 32}; write_struct(fp, TYPE_USER, &user1); write_struct(fp, TYPE_PRODUCT, &prod1); write_struct(fp, TYPE_USER, &user2); fclose(fp); return 0; }
步骤3:读取二进制文件的实现
读取时先读类型标记,再根据标记选择对应结构体解析:
// 读取单个结构体从文件 int read_struct(FILE *fp) { StructType type; // 先读取类型标记 if (fread(&type, sizeof(StructType), 1, fp) != 1) { // 如果是文件末尾,返回0表示读取完成 if (feof(fp)) return 0; perror("Failed to read struct type"); return -1; } // 根据类型读取并解析数据 switch(type) { case TYPE_USER: { User user; if (fread(&user, sizeof(User), 1, fp) != 1) { perror("Failed to read User"); return -1; } printf("Read User: ID=%u, Name=%s, Age=%u\n", user.id, user.name, user.age); break; } case TYPE_PRODUCT: { Product prod; if (fread(&prod, sizeof(Product), 1, fp) != 1) { perror("Failed to read Product"); return -1; } printf("Read Product: ID=%u, Price=%.1f, Stock=%u\n", prod.product_id, prod.price, prod.stock); break; } default: fprintf(stderr, "Unknown struct type in file\n"); return -1; } return 1; } // 示例:读取整个文件 int main() { FILE *fp = fopen("data.bin", "rb"); if (!fp) { perror("Failed to open file"); return 1; } int ret; // 循环读取直到文件末尾 while ((ret = read_struct(fp)) == 1); if (ret == -1) { fclose(fp); return 1; } fclose(fp); return 0; }
必须注意的坑点
- 结构体内存对齐:不同编译器、不同编译选项下,结构体的内存布局可能不同(比如
#pragma pack会改变对齐方式)。如果要跨平台使用,建议手动指定对齐规则,或者把结构体拆成单个字段逐个读写(虽然麻烦但更可靠)。 - 字节序问题:如果你的程序要跨不同字节序的平台(比如x86是小端,PowerPC是大端),不能直接读写整数、浮点数等类型,需要先转成网络字节序(用
htons/htonl等函数),读取时再转回来。 - 字符串长度:如果结构体里有可变长度字符串,不要直接存char数组,应该先存字符串长度,再存字符串内容,不然读取时不知道该读多少字节。
内容的提问来源于stack exchange,提问作者Sadi
相关产品推荐
相关产品推荐

