C语言结构体与指针实现学生数据文本存储程序调试求助(含增删查显功能)
我仔细看了你的学生数据管理程序代码,确实在指针使用、文件操作和逻辑设计上有不少问题,导致功能无法正常运行。下面咱们逐个排查并解决这些问题:
标准C的main函数不能这么定义:void main(INFO *p, INFO *px),这个写法不符合语法规范,而且你调用子函数时传递的p和px都是未初始化的野指针,直接访问p->nome这类成员肯定会触发内存错误。
修改后的主函数用循环替代递归调用,同时给指针动态分配内存,避免野指针:
int main(void) { int opc; INFO *p = NULL; do { menu(); printf(" (-) Introduza a opcao (-)\n > "); scanf("%d", &opc); // 清除输入缓冲区的换行符,避免后续fgets读取异常 while(getchar() != '\n'); switch(opc) { case 1: p = (INFO*)malloc(sizeof(INFO)); if(p == NULL) { printf("Erro ao alocar memória!\n"); return 1; } registar(p); break; case 2: listar(); break; case 3: deleteI(); break; case 4: find(); break; case 0: printf("Saindo do programa...\n"); break; default: printf("Opção inválida!\n"); } } while(opc != 0); // 程序退出前释放链表内存(如果需要维护链表) INFO *temp; while(inicio != NULL) { temp = inicio; inicio = inicio->px; free(temp); } return 0; }
你的registar函数打开的是"INFO"文件,但其他功能函数都读"DADOS"文件,这就导致存入的数据根本读不到;更关键的是,registar里打开文件后完全没有把学生数据写入文件,等于白输入!
修改后的registar函数统一文件名,加入写入逻辑:
void registar(INFO *p) { FILE *fp = fopen("DADOS", "ab"); // 用二进制追加模式,避免文本模式的格式问题 if(fp == NULL) { printf("Erro ao abrir arquivo!\n"); free(p); return; } // 输入学生信息的代码保留,这里省略 // 提前计算平均分和状态,一起写入文件 p->media = mcalc(p->nota1, p->nota2, p->nota3); strcpy(p->estado, p->media < 10 ? "Reprovado" : "Aprovado"); // 将结构体数据写入文件 fwrite(p, sizeof(INFO), 1, fp); fclose(fp); // 链表维护逻辑保留 p->px = NULL; if(inicio == NULL) { inicio = p; } else { INFO *seg = inicio; while(seg->px != NULL) { seg = seg->px; } seg->px = p; } printf("\n(-) Aluno registado com sucesso! (-)\n"); printf("(-) Pressione qualquer tecla para regressar ao menu principal. (-)\n"); getchar(); system("cls"); }
比如listar函数里,你直接用fread(&INFO, sizeof(INFO), 1, fp1),INFO是结构体类型不是变量,这是语法错误;而且你一直在用野指针p的成员,根本没用到从文件读取的数据。
修改后的listar函数用临时变量存储读取的内容:
void listar() { FILE *fp1 = fopen("DADOS", "rb"); if(fp1 == NULL) { printf("Não há dados registados ou erro ao abrir arquivo!\n"); getchar(); return; } INFO temp; while(fread(&temp, sizeof(INFO), 1, fp1)) { printf("\n (-) Nome: %s (-)", temp.nome); printf("\n (-) Turma: %s (-)", temp.turma); printf("\n (-) 1º Nota: %.2f (-)", temp.nota1); printf("\n (-) 2º Nota: %.2f (-)", temp.nota2); printf("\n (-) 3º Nota: %.2f (-)", temp.nota3); printf("\n (-) Media: %.2f (-)", temp.media); printf("\n (-) Estado: %s (-)\n", temp.estado); } fclose(fp1); printf("\n(-) Pressione qualquer tecla para regressar ao menu principal. (-)\n"); getchar(); }
==代替strcmp 在find和deleteI函数里,你用s == r比较字符串,这是比较两个指针的地址,不是字符串内容,必须用strcmp函数。同时替换不安全的gets为fgets:
修改后的find函数:
void find() { FILE *fp2 = fopen("DADOS", "rb"); if(fp2 == NULL) { printf("Não há dados registados!\n"); getchar(); return; } char n[MAX]; printf("\n (-) Introduza o nome do aluno que pretende encontrar:\n > "); fgets(n, MAX, stdin); // 去掉fgets读取的换行符 n[strcspn(n, "\n")] = '\0'; INFO temp; int encontrado = 0; while(fread(&temp, sizeof(INFO), 1, fp2)) { if(strcmp(temp.nome, n) == 0) { encontrado = 1; printf("\n (-) Nome: %s (-)", temp.nome); printf("\n (-) Turma: %s (-)", temp.turma); printf("\n (-) 1º Nota: %.2f (-)", temp.nota1); printf("\n (-) 2º Nota: %.2f (-)", temp.nota2); printf("\n (-) 3º Nota: %.2f (-)", temp.nota3); printf("\n (-) Media: %.2f (-)", temp.media); printf("\n (-) Estado: %s (-)\n", temp.estado); break; // 找到第一个匹配项就退出,如需找所有可去掉break } } if(!encontrado) { printf("\n (-) O aluno não foi encontrado! (-)\n"); } fclose(fp2); printf("\n(-) Pressione qualquer tecla para regressar ao menu principal. (-)\n"); getchar(); }
你的mcalc函数连续调用floorf、roundf、ceilf,这会导致结果完全不符合预期,比如本来要四舍五入到两位小数,结果被多次取整打乱。修改后只保留四舍五入逻辑:
float mcalc(float a, float b, float c) { float med = (a + b + c) / 3.0f; // 四舍五入到两位小数 med = roundf(med * 100.0f) / 100.0f; return med; }
deleteI函数里同样存在字符串比较错误,而且scanf("%s", &r)是语法错误(r是数组名,直接传r即可)。修改后的删除函数更简洁可靠:
void deleteI() { FILE *fpo = fopen("DADOS", "rb"); if(fpo == NULL) { printf("Não há dados registados!\n"); getchar(); return; } FILE *fpt = fopen("temp", "wb"); if(fpt == NULL) { printf("Erro ao criar arquivo temporário!\n"); fclose(fpo); getchar(); return; } char r[MAX]; printf("\n (-) Introduza o nome do aluno que pretende apagar:\n > "); fgets(r, MAX, stdin); r[strcspn(r, "\n")] = '\0'; INFO temp; int encontrado = 0; while(fread(&temp, sizeof(INFO), 1, fpo)) { if(strcmp(temp.nome, r) != 0) { fwrite(&temp, sizeof(INFO), 1, fpt); } else { encontrado = 1; } } fclose(fpo); fclose(fpt); // 替换原文件 remove("DADOS"); rename("temp", "DADOS"); if(encontrado) { printf("\n (-) O aluno foi apagado com sucesso! (-)\n"); } else { printf("\n (-) O aluno não foi encontrado! (-)\n"); remove("temp"); // 删除无用的临时文件 } printf("\n(-) Pressione qualquer tecla para regressar ao menu principal. (-)\n"); getchar(); }
- 菜单里重复了一行
| [0] - Sair |,记得删除其中一行; fflush(stdin)在部分编译器属于未定义行为,建议用while(getchar() != '\n');清除输入缓冲区;- 结构体里的
*p成员未被使用,可以删除,把*px改名为*next更易读。
把这些修改整合后,你的程序应该就能正常实现注册、显示、查询、删除的功能了,记得测试每个流程确保数据处理正确。
内容的提问来源于stack exchange,提问作者user0l

