C语言fwrite/fread结构体指针程序仅首次正常,二次写入后无法读取求助
结构体二进制读写异常的问题分析与解决
问题根源
你的代码核心问题出在结构体存储字符指针而非字符串内容:
StudentFile中的name、role、course是字符指针,fwrite写入文件的是这些指针的内存地址,不是字符串的实际内容。- 首次运行时,指针指向的是程序内存中的字符串常量地址,程序未退出时地址有效,所以读取后能正常打印;但程序退出后,这些地址会被系统回收,再次运行时写入的是新的指针地址,读取时用旧地址访问会触发未定义行为(比如乱码、无输出),看起来像是“无法读取内容”。
- 追加写入的需求本身可以通过
"ab"模式实现,但因为指针地址的问题,即使追加了无效的地址数据,也无法得到预期的追加效果。
修正方案
最直接的解决方式是把结构体中的字符指针改为固定长度的字符数组,这样fwrite会直接写入字符串内容,读取时能正确还原数据。如果需要动态长度的字符串,可以手动写入字符串长度+内容,但固定数组的方式更简单易用。
修正后的完整代码
#include <stdio.h> #include <stdlib.h> #include <string.h> // 将字符指针改为固定长度字符数组,可根据实际需求调整长度 typedef struct { char name[50], role[50], course[50]; int year, id; } StudentFile; void saveBin(StudentFile *studentsFile, int lines){ FILE *file = fopen("studentsx.bin","ab"); if (!file) { printf("\n\n\t无法打开文件。 \n\n"); exit(1); } // 一次性写入整个结构体数组,效率更高 fwrite(studentsFile, sizeof(StudentFile), lines, file); fclose(file); } void readBin(){ StudentFile *studentsFile = malloc(sizeof(StudentFile)*5000); if (!studentsFile) { printf("\n\n\t内存分配失败。 \n\n"); exit(1); } FILE *file = fopen("studentsx.bin","rb"); if (!file) { printf("\n\n\t无法打开文件。 \n\n"); free(studentsFile); exit(1); } int j = 0; // 循环读取直到文件结束 while (fread(&studentsFile[j], sizeof(StudentFile), 1, file)){ printf("\n第%d条记录:%s\t%s\t%d\t%d\t%s", j+1, studentsFile[j].name, studentsFile[j].role, studentsFile[j].year, studentsFile[j].id, studentsFile[j].course); j++; } fclose(file); free(studentsFile); // 释放分配的内存,避免泄漏 } int main(){ // 遵循C标准,main函数返回int类型 StudentFile *studentsFile = malloc(sizeof(StudentFile)*2); if (!studentsFile) { printf("\n\n\t内存分配失败。 \n\n"); exit(1); } int lines = 2; // 使用strcpy将字符串复制到结构体数组中 strcpy(studentsFile[0].name, "John"); strcpy(studentsFile[0].role, "Gamer"); studentsFile[0].year = 1999; studentsFile[0].id = 1; strcpy(studentsFile[0].course, "IOT"); strcpy(studentsFile[1].name, "Piter"); strcpy(studentsFile[1].role, "GamerXL"); studentsFile[1].year = 1991; studentsFile[1].id = 2; strcpy(studentsFile[1].course, "IOTXL"); saveBin(studentsFile, lines); readBin(); free(studentsFile); // 释放内存 return 0; }
关键说明
- 字符数组替代指针:确保写入文件的是字符串实际内容,而非无效的内存地址,解决了跨运行读取失败的问题。
- 标准main函数:将
void main改为int main并返回0,符合C语言标准规范。 - 内存管理:添加内存分配失败检查,使用完内存后及时释放,避免内存泄漏。
- 追加功能:
"ab"模式会自动将数据追加到文件末尾,多次运行程序会持续添加新的学生记录,读取时能一次性读出所有历史数据。
内容的提问来源于stack exchange,提问作者Pedro Tavares
相关产品推荐
相关产品推荐

