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

C语言用二级指针结构体读取CSV文件出现段错误与Invalid read报错

代码存在以下核心错误,直接导致段错误和内存问题:

  • 1. 空指针解引用错误
    主逻辑中v初始化为NULL,C语言函数参数为值传递,read_csv内部对形参v的赋值不会修改外部实参v的值,循环内直接访问v->make等成员属于解引用空指针,直接触发段错误。且这部分free(v)逻辑完全多余,read_csv已经返回了分配好的结构体指针,直接存到car数组即可,不需要用v承接。
  • 2. 二级指针数组未扩容,内存越界
    初始仅给car分配了1个vehicle_t*的存储空间,后续num_cars自增后,访问*(car+num_cars)属于越界写内存,会破坏堆结构,这是Valgrind报Invalid read of size 8的核心原因。需要每次新增车辆前用realloc给car扩容。
  • 3. 循环逻辑、数组下标错误
    • 主循环while(!fgets(buffer, BUF_SIZE, fp))条件写反:fgets读取成功返回非空指针,加!会导致读取成功时不进入循环,且read_csv内部又重复调用了fgets,属于重复读取,外层的fgets完全多余。
    • 打印、释放的循环条件用了i <= num_cars,数组下标范围应为0 ~ num_cars - 1,<=会导致访问越界。且循环内全程使用*car(等价于car[0]),没有用到下标i,永远只访问第一个元素,也不会遍历所有车辆。
  • 4. 内存泄漏与重复释放问题
    • 主循环中先给*(car+num_cars)分配了vehicle_t结构体空间,马上又用read_csv的返回值覆盖该指针,刚分配的空间直接泄漏。
    • 释放逻辑中每次循环都调用free(car),第一次循环就释放了二级指针本身,后续循环访问car属于野指针访问。car只需要在所有结构体释放完成后释放一次即可。
  • 5. read_csv字段赋值逻辑错误
    字段读取顺序混乱,以make字段为例:
    tok = strtok(NULL, ","); // 读取到make字段
    v->make = calloc(strlen(tok)+1, sizeof(char));
    tok = strtok(NULL, ","); // 已经跳到model字段
    strcpy(v->make, tok); // 错误把model内容复制到make
    
    正确逻辑是分配空间后立刻复制当前tok的内容,再读取下一个tok。同时需要判断strtok返回值是否为NULL,避免CSV格式错误时访问空指针。

修正后的核心逻辑示例:

vehicle_t **car = NULL;
int num_cars = 0;
char buffer[BUF_SIZE] = {0};

while (fgets(buffer, BUF_SIZE, fp)) {
    // 给二级指针数组扩容
    car = realloc(car, (num_cars + 1) * sizeof(vehicle_t*));
    // 直接传入读取到的行缓存,不用在read_csv里再调用fgets
    car[num_cars] = read_csv(buffer);
    num_cars++;
}

// 打印逻辑
for(int i = 0; i < num_cars; i++){
    printf("%d %s %s (%s) LIC:%s\n", car[i]->year, car[i]->make, car[i]->model, car[i]->color, car[i]->license_plate);
}

// 释放逻辑
for(int i = 0; i < num_cars; i++){
    free(car[i]->make);
    free(car[i]->model);
    free(car[i]->color);
    free(car[i]->license_plate);
    free(car[i]);
}
free(car);

修正后的read_csv示例:

vehicle_t *read_csv(char *buffer){
    vehicle_t *v = calloc(1, sizeof(vehicle_t));
    char *tok = strtok(buffer, ",");
    if (!tok) goto err;
    v->year = atoi(tok);

    tok = strtok(NULL, ",");
    if (!tok) goto err;
    v->make = calloc(strlen(tok)+1, sizeof(char));
    strcpy(v->make, tok);

    tok = strtok(NULL, ",");
    if (!tok) goto err;
    v->model = calloc(strlen(tok)+1, sizeof(char));
    strcpy(v->model, tok);

    tok = strtok(NULL, ",");
    if (!tok) goto err;
    v->color = calloc(strlen(tok)+1, sizeof(char));
    strcpy(v->color, tok);

    tok = strtok(NULL, ",\n"); // 最后一个字段要匹配换行符,去掉行尾换行
    if (!tok) goto err;
    v->license_plate = calloc(strlen(tok)+1, sizeof(char));
    strcpy(v->license_plate, tok);

    return v;
err:
    // 出错时释放已分配的内存,避免泄漏
    free(v->make);
    free(v->model);
    free(v->color);
    free(v->license_plate);
    free(v);
    return NULL;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 00:54:03