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的内容,再读取下一个tok。同时需要判断tok = strtok(NULL, ","); // 读取到make字段 v->make = calloc(strlen(tok)+1, sizeof(char)); tok = strtok(NULL, ","); // 已经跳到model字段 strcpy(v->make, tok); // 错误把model内容复制到makestrtok返回值是否为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
相关产品推荐
相关产品推荐

