C语言使用fread读取二进制文件结构体位域字段解析问题
问题核心原因
- 位域不能单独执行
fread读取:C语言中的位域是按比特位共享底层存储单元的,你定义的4个1位宽的位域总共仅占用4比特空间,打包在同一个字节中,单独对位域成员取地址、调用sizeof、执行fread都是不符合规范的操作,会导致文件读取偏移完全错位。 - 打印格式符错误:原
printf语句中四个位域对应的位置写了无效的%占位符,没有指定输出格式。 - 结构体默认填充对齐:编译器默认会在结构体成员之间插入空白填充字节,来优化内存访问效率,会导致你按单个成员大小依次读取的内容和实际内存布局不匹配,字段值全部错乱。
修复方案
第一步:修改结构体为紧凑无填充布局,调整位域定义
使用编译器指令关闭结构体填充,同时将位域的底层存储类型改为unsigned char,适配4比特的存储需求:
#define MAX_HAT 9 #include<stdio.h> #include<stdlib.h> // 按1字节对齐,关闭结构体填充 #pragma pack(1) struct test { short int land; float experience; char boys; short int angle; double industry; int thread; long int shoe; float kitty; unsigned char price; // 4个1位宽位域,共占4比特,和你提到的0xd(二进制1101)对应 unsigned char father: 1; unsigned char room: 1; unsigned char attraction: 1; unsigned char cake: 1; // 如果文件中该字节剩余4位无意义,可以加reserved占坑:unsigned char reserved:4; int foot; char hat[MAX_HAT]; char nest; float bean; }; // 恢复编译器默认对齐规则 #pragma pack()
第二步:修改读取逻辑,一次性读入整个结构体
不用单独读取每个成员,直接读整个结构体即可,避免偏移错误:
int main(int argc, char **argv) { struct test t1; FILE *fp; if (argc < 2) { fprintf(stderr, "Usage: %s input_file\n", argv[0]); exit(1); } fp = fopen(argv[1], "rb"); if (fp == NULL){ fprintf(stderr, "Cannot open the file %s\n", argv[1]); exit(1); } printf("land, experience, boys, angle, industry, thread, shoe, kitty, price, father, room, attraction, cake, foot, hat, nest, bean \n"); // 一次性读入整个结构体,替代之前的多次fread while(fread(&t1, sizeof(struct test), 1, fp) == 1) { // 四个位域用%u输出即可,只会显示0或1 printf("%d, %f, %i, %i, %f, %d, %ld, %f, %u, %u, %u, %u, %u, %x, %s, %c, %f\n", t1.land, t1.experience, t1.boys, t1.angle, t1.industry, t1.thread, t1.shoe, t1.kitty, t1.price, t1.father, t1.room, t1.attraction, t1.cake, t1.foot, t1.hat, t1.nest, t1.bean); } fclose(fp); return 0; }
第三步:调整位域顺序适配文件比特布局
如果读出来的四个位域值顺序和预期的1、1、0、1不符,调整结构体中四个位域的定义顺序即可,因为C标准没有规定位域在字节中的排列顺序,会根据编译器、大小端环境变化,反过来定义就能适配你文件中的比特顺序。
内容的提问来源于stack exchange,提问作者staypersistent
相关产品推荐
相关产品推荐

