Visual C读写含结构体数据的二进制文件时出现异常行为
程序错误分析与修复
你的代码存在多个核心错误,直接引发堆损坏、断点触发等问题,以下是具体问题和修复方案:
一、内存分配错误
所有涉及Employee数组的内存分配,都错误使用了sizeof(db->employee)——这是指针的大小(32位系统4字节,64位8字节),而非Employee结构体的实际大小。比如:
main中db->employee = (Employee*)malloc(db->size * sizeof(db->employee));LoadDatabase中db->employee = (Employee*)malloc(n * sizeof(db->employee));
修复:替换为sizeof(Employee)或sizeof(*db->employee),确保分配足够存储每个员工数据的内存。
二、二进制文件读写错误
保存数据时
SaveDatabase中fwrite(&db->employee, sizeof(db->employee), db->size, fp)是错误的:你写入的是指针变量的地址值,而非指针指向的员工数组数据。
修复:改为fwrite(db->employee, sizeof(Employee), db->size, fp),直接写入数组的实际内容。
加载数据时
LoadDatabase中fread(&db->employee, sizeof(db->employee), n, fp)同样错误:你试图把文件内容写入指针变量本身,而非指针指向的已分配内存。另外,加载后未给db->size赋值,后续循环会访问未初始化的垃圾值。
修复:改为fread(db->employee, sizeof(Employee), n, fp),同时添加db->size = n;赋值员工数量。
三、内存泄漏与文件打开错误处理
LoadDatabase中先malloc了db,再尝试打开文件,如果文件打开失败,直接返回NULL会导致db的内存无法释放,造成泄漏。
修复:先打开文件,确认成功后再mallocdb。
四、其他潜在问题
strcpy存在缓冲区溢出风险,建议替换为strncpy并手动添加字符串结束符;printf中的/t是笔误,应该是\t制表符;- 所有malloc操作未做空指针检查,极端情况下会引发崩溃。
修复后的完整代码
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string.h> #define SAVE typedef struct Employee { char id[6]; char surname[20]; char lastname[20]; char firstname[20]; unsigned int basic; unsigned int incentive; unsigned int salary; } Employee; typedef struct Database { Employee* employee; unsigned int size; } Database; int SaveDatabase(const char* fileName, Database* db) { FILE* fp = fopen(fileName, "wb"); if (fp == NULL) return 1; if (fwrite(&db->size, sizeof(db->size), 1, fp) != 1) { fclose(fp); return 2; } if (fwrite(db->employee, sizeof(Employee), db->size, fp) != db->size) { fclose(fp); return 3; } fclose(fp); return 0; } Database* LoadDatabase(const char* fileName) { FILE* fp = fopen(fileName, "rb"); if (fp == NULL) return NULL; unsigned int n = 0; if (fread(&n, sizeof(n), 1, fp) != 1) { fclose(fp); return NULL; } Database* db = (Database*)malloc(sizeof(Database)); if (db == NULL) { fclose(fp); return NULL; } db->employee = (Employee*)malloc(n * sizeof(Employee)); if (db->employee == NULL) { free(db); fclose(fp); return NULL; } if (fread(db->employee, sizeof(Employee), n, fp) != n) { free(db->employee); free(db); fclose(fp); return NULL; } db->size = n; fclose(fp); return db; } int main(int argc, char* argv[]) { #ifdef SAVE Database* db = (Database*)malloc(sizeof(Database)); if (db == NULL) return 1; db->size = 2; db->employee = (Employee*)malloc(db->size * sizeof(Employee)); if (db->employee == NULL) { free(db); return 1; } strncpy(db->employee[0].id, "1234", sizeof(db->employee[0].id)-1); db->employee[0].id[sizeof(db->employee[0].id)-1] = '\0'; strncpy(db->employee[0].surname, "Bakshi", sizeof(db->employee[0].surname)-1); db->employee[0].surname[sizeof(db->employee[0].surname)-1] = '\0'; strncpy(db->employee[0].lastname, "Vipin", sizeof(db->employee[0].lastname)-1); db->employee[0].lastname[sizeof(db->employee[0].lastname)-1] = '\0'; strncpy(db->employee[0].firstname, "Byomkesh", sizeof(db->employee[0].firstname)-1); db->employee[0].firstname[sizeof(db->employee[0].firstname)-1] = '\0'; db->employee[0].basic = 25000; db->employee[0].incentive = 25000; db->employee[0].salary = 50000; strncpy(db->employee[1].id, "1235", sizeof(db->employee[1].id)-1); db->employee[1].id[sizeof(db->employee[1].id)-1] = '\0'; strncpy(db->employee[1].surname, "Chatterjee", sizeof(db->employee[1].surname)-1); db->employee[1].surname[sizeof(db->employee[1].surname)-1] = '\0'; strncpy(db->employee[1].lastname, "Naren", sizeof(db->employee[1].lastname)-1); db->employee[1].lastname[sizeof(db->employee[1].lastname)-1] = '\0'; strncpy(db->employee[1].firstname, "Basu", sizeof(db->employee[1].firstname)-1); db->employee[1].firstname[sizeof(db->employee[1].firstname)-1] = '\0'; db->employee[1].basic = 35000; db->employee[1].incentive = 30000; db->employee[1].salary = 65000; SaveDatabase("Data.dat", db); free(db->employee); free(db); #else Database* db = LoadDatabase("Data.dat"); if (db == NULL) { printf("加载数据库失败\n"); return 1; } for (unsigned int i = 0; i < db->size; i++) { printf("\n%s\t%s %s %s %u %u %u", db->employee[i].id, db->employee[i].firstname, db->employee[i].lastname, db->employee[i].surname, db->employee[i].basic, db->employee[i].incentive, db->employee[i].salary); } free(db->employee); free(db); #endif // SAVE return 0; }
内容的提问来源于stack exchange,提问作者nascent
相关产品推荐
相关产品推荐

