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

C语言读取文件并存储为结构体数组的问题求助

问题描述

我有一个存储学生数据的文件,格式如下:

ID avg_scores first_name last_name city department

定义的student结构体如下:

typedef struct student {
    int student_id;
    int avg_scores;
    char* first_name;
    char* last_name;
    char* city;
    char* department;
} *student;

我希望逐行读取文件并将数据存储为student结构体数组,但在char*类型字段的处理上遇到问题,想请教遗漏了什么?
另外,为什么必须为这些char*字段单独调用malloc?不这么做就无法正常运行,我对此不太理解,而且我也不知道每个字段的具体长度。

以下是我编写的函数:

student * getStudents(FILE * students, int numberOfStudents){
    student * studentsPtr = malloc(numberOfStudents * sizeof(student));

    for (int i=0; i<numberOfStudents; i++){
        studentsPtr[i] = malloc(sizeof(student));
        studentsPtr[i]->first_name = malloc(20*sizeof(char));
        studentsPtr[i]->last_name = malloc(20*sizeof(char));
        studentsPtr[i]->city = malloc(20*sizeof(char));
        studentsPtr[i]->department = malloc(20*sizeof(char));
        fscanf(students, "%d %d %s %s %s %s", &studentsPtr[i]->student_id,
               &studentsPtr[i]->avg_scores, studentsPtr[i]->first_name,
               studentsPtr[i]->last_name, studentsPtr[i]->city, studentsPtr[i]->department);
    }
    return studentsPtr;
}

问题解答

一、你的代码里遗漏的问题

  • 结构体语法错误:int avg_scores后面少了分号,这会直接导致编译失败。
  • 结构体内存分配错误:studentsPtr[i] = malloc(sizeof(student));里的student是指针类型,sizeof(student)得到的是指针的大小(通常8字节),而非结构体本身的大小。应该改成malloc(sizeof(struct student));,你需要分配整个结构体的内存空间,不是指针的空间。
  • 固定长度分配的风险:你给每个字符串字段硬分配20字节,如果文件里的字符串超过19字符(加上终止符\0),fscanf会写入超出分配的内存,触发程序崩溃、数据损坏等未定义行为。
  • 未检查malloc返回值:任何malloc都可能因内存不足返回NULL,不检查的话后续操作会直接触发空指针错误。

二、为什么必须为char*字段调用malloc?

char*只是一个指针变量,它本身不存储任何字符串内容,只用来保存一块内存的地址。如果不为它分配内存:

  • 指针的值是随机的「野指针」,fscanf往里面写数据相当于往未知的内存区域写入,轻则程序崩溃,重则破坏其他程序的内存数据,属于严重的未定义行为。
  • 只有调用malloc(或者用strdup这类封装好的函数),才能在堆上开辟一块专门的内存空间,让char*指向这块空间,用来存储字符串的实际内容。

三、不知道字段长度的解决办法

如果不确定每个字符串的长度,可以用两种方式处理:

  1. 临时缓冲区中转:
    先定义一个足够大的临时字符数组(比如256字节,覆盖绝大多数场景),用fscanf把字符串读入临时数组,再用strlen获取实际长度,最后分配对应大小的内存(记得加1字节存\0),用strcpy把临时数组的内容复制过去。示例:
    char temp[256];
    fscanf(students, "%s", temp);
    studentsPtr[i]->first_name = malloc(strlen(temp) + 1);
    strcpy(studentsPtr[i]->first_name, temp);
    
  2. 限制fscanf读取长度:
    在格式字符串里指定最大读取长度,比如%19s表示最多读19个字符(留1字节存\0),这样即使原字符串更长,也只会截断到19字符,不会超出你分配的20字节内存。但这种方式会丢失超长字符串的内容,适合能确定字段最大长度的场景。

四、修正后的示例代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct student {
    int student_id;
    int avg_scores;
    char* first_name;
    char* last_name;
    char* city;
    char* department;
} *student;

student * getStudents(FILE * students, int numberOfStudents){
    // 分配结构体指针数组的内存
    student * studentsPtr = malloc(numberOfStudents * sizeof(student));
    if (!studentsPtr) return NULL;

    for (int i=0; i<numberOfStudents; i++){
        // 分配单个结构体的内存,用struct student的大小
        studentsPtr[i] = malloc(sizeof(struct student));
        if (!studentsPtr[i]) {
            // 内存分配失败时,释放已分配的资源避免泄漏
            for (int j=0; j<i; j++) {
                free(studentsPtr[j]->first_name);
                free(studentsPtr[j]->last_name);
                free(studentsPtr[j]->city);
                free(studentsPtr[j]->department);
                free(studentsPtr[j]);
            }
            free(studentsPtr);
            return NULL;
        }

        char temp[256];
        // 读取ID和平均分,检查读取是否成功
        if (fscanf(students, "%d %d", &studentsPtr[i]->student_id, &studentsPtr[i]->avg_scores) != 2) {
            // 实际开发中要在这里处理读取失败的清理逻辑
            break;
        }

        // 处理first_name
        fscanf(students, "%s", temp);
        studentsPtr[i]->first_name = malloc(strlen(temp) + 1);
        if (!studentsPtr[i]->first_name) { /* 处理分配失败 */ }
        strcpy(studentsPtr[i]->first_name, temp);

        // 处理last_name
        fscanf(students, "%s", temp);
        studentsPtr[i]->last_name = malloc(strlen(temp) + 1);
        if (!studentsPtr[i]->last_name) { /* 处理分配失败 */ }
        strcpy(studentsPtr[i]->last_name, temp);

        // 处理city
        fscanf(students, "%s", temp);
        studentsPtr[i]->city = malloc(strlen(temp) + 1);
        if (!studentsPtr[i]->city) { /* 处理分配失败 */ }
        strcpy(studentsPtr[i]->city, temp);

        // 处理department
        fscanf(students, "%s", temp);
        studentsPtr[i]->department = malloc(strlen(temp) + 1);
        if (!studentsPtr[i]->department) { /* 处理分配失败 */ }
        strcpy(studentsPtr[i]->department, temp);
    }
    return studentsPtr;
}

内容的提问来源于stack exchange,提问作者Ben

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 02:52:52