无法从二进制文件正确读取结构体数据,求技术修复方案
首先,你的程序出现ptr[0][0].Flat_ID输出随机数的核心原因是在memalloc函数中提前关闭了文件,导致后续读取结构体的操作使用了无效的文件指针,同时还有几个其他关键bug影响程序运行,下面逐一解决:
1. 致命错误:memalloc中提前关闭文件导致读取无效
在你的memalloc函数中,读取完floors和flats_per_floor后立刻关闭了文件,后续尝试用已经关闭的f指针读取结构体,这会触发未定义行为(比如读取随机垃圾值)。同时还存在双重关闭文件的问题。
错误代码片段:
fread(&floors,sizeof(unsigned),1,f); fread(&flats_per_floor,sizeof(unsigned),1,f); fclose(f); // 提前关闭文件! // 后续用无效的f指针读取结构体 for(int i = 0; i < floors; i++) { for(int j = 0; j < flats_per_floor; j++) { fread(&Apartment,sizeof(S_Apartament),1,f); arr[i][j]=Apartment; } } fclose(f); // 再次关闭已关闭的文件,触发错误
修复后的memalloc函数:
S_Apartament** memalloc(unsigned int *floors, unsigned int *flats_per_floor, S_Apartament Apartment, char FileName[50]) { FILE *f = fopen(FileName,"rb"); if(f==NULL) { printf("Error opening file, or no such file.\n"); // 注意:f为NULL时不能调用fclose,直接退出即可 exit(1); } // 读取楼层和每层户数(用指针传递,让main函数的变量也能更新) fread(floors, sizeof(unsigned int), 1, f); fread(flats_per_floor, sizeof(unsigned int), 1, f); // 分配二维数组内存 S_Apartament **arr = (S_Apartament **)malloc(sizeof(S_Apartament*) * *floors); for (int i = 0; i < *floors; i++) { arr[i] = (S_Apartament *)malloc(sizeof(S_Apartament) * *flats_per_floor); } // 读取所有结构体(此时文件仍处于打开状态) for(int i = 0; i < *floors; i++) { for(int j = 0; j < *flats_per_floor; j++) { fread(&arr[i][j], sizeof(S_Apartament), 1, f); } } fclose(f); // 所有读取完成后再关闭文件 return arr; }
2. 关键错误:值传递导致main中floors和flats_per_floor未更新
你在main中调用memalloc时传递的是floors和flats_per_floor的初始值0,而C语言是值传递,memalloc内部修改的只是局部变量,main中的变量不会被更新,导致后续调用enterNewResidents等函数时使用的是错误的0值。
修复main中的调用:
// 把原来的调用改成传递指针 S_Apartament **ptr = memalloc(&floors, &flats_per_floor, Flats, FileName); // 此时floors和flats_per_floor已经被正确更新为文件中的值 printf("%u\n", ptr[0][0].Flat_ID);
同时修改Header.h中的函数声明:
// 把参数改成指针类型 S_Apartament** memalloc(unsigned int *floors, unsigned int *flats_per_floor, S_Apartament Apartment, char FileName[50]);
3. 循环条件错误:calculateTax中的无限循环风险
在calculateTax函数中,内层循环的条件写错了,用了i<flats_per_floor而不是j<flats_per_floor,这会导致无限循环或者数组越界。
错误代码:
for(int j=0; i<flats_per_floor; j++) {
修复后:
for(int j=0; j<flats_per_floor; j++) {
4. 错误处理优化:避免关闭NULL指针
在memalloc和writeInfo中,当fopen失败时,你尝试关闭NULL指针,这会触发未定义行为,应该直接退出而不调用fclose。
修复writeInfo中的错误处理:
if(fp==NULL) { printf("Error opening file\n"); // 去掉fclose(fp),因为fp是NULL exit(2); }
5. emptyApartment中的越界访问风险
当找到目标公寓后,内层循环break,此时j的值等于flats_per_floor,外层循环中ptr[i][j]会访问数组越界,建议用标志位来控制循环:
修复后的emptyApartment:
void emptyApartment(S_Apartament **ptr, unsigned floors, unsigned flats_per_floor) { unsigned short Entered_number = 0; int i = 0, j = 0; int found = 0; // 标志位,标记是否找到公寓 printf("Enter the number of the apartment you wish to empty:\n"); scanf("%hu", &Entered_number); for(i=0; i<floors; i++) { for(j=0; j<flats_per_floor; j++) { if(Entered_number == ptr[i][j].Flat_ID) { ptr[i][j].count_Adults = 0; ptr[i][j].count_Children = 0; ptr[i][j].Family_Surname[0] = '\0'; ptr[i][j].rent = 0.00; printf("Apartment is empty\n"); found = 1; break; } } if(found) { break; } } if(!found) { printf("No such apartment\n"); } }
最后:main中的内存释放问题
你在menu循环内部调用了free(ptr);,但如果用户多次选择菜单命令,ptr会被提前释放,后续操作会访问已释放的内存。应该把free(ptr);移到menu循环结束之后:
while(1) { choice = menu(); if(choice==0) { break; } switch(choice) { case 1: enterNewResidents(ptr, floors, flats_per_floor); break; case 2: calculateTax(ptr, floors, flats_per_floor); break; case 3: Elevator(ptr, floors, flats_per_floor); break; case 4: emptyApartment(ptr, floors, flats_per_floor); break; default: printf("Invalid command\n"); break; } writeInfo(ptr,floors,flats_per_floor, FileName); } free(ptr); // 移到循环外面,避免提前释放
经过以上修复后,你的程序应该能正确读取二进制文件中的结构体数据,ptr[0][0].Flat_ID会输出预期的101,其他功能也能正常运行。
内容的提问来源于stack exchange,提问作者Jordan Baller

