You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言结构体与指针实现学生数据文本存储程序调试求助(含增删查显功能)

我仔细看了你的学生数据管理程序代码,确实在指针使用、文件操作和逻辑设计上有不少问题,导致功能无法正常运行。下面咱们逐个排查并解决这些问题:

1. 主函数参数错误导致野指针崩溃

标准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;
}
2. 文件存储核心逻辑缺失:存读文件不一致+未写入数据

你的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");
}
3. 指针与文件读取逻辑错误

比如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();
}
4. 字符串比较错误:用==代替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();
}
5. 平均分计算逻辑混乱

你的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;
}
6. 删除函数的逻辑与语法错误

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 19:02:31