C语言CSV解析函数访问已分配数组出现Segmentation fault段错误
问题核心原因与修复方案
你的代码存在多处内存管理和参数传递的逻辑错误,直接导致段错误:
- 参数传递层级错误:你需要在函数内修改外部传入的
char*数组指针本身,所以入参应该声明为char*** data,而不是char** data。当前写法下函数内对data的赋值仅修改局部变量,外部实参不会拿到你分配的内存地址,传入NULL的情况下直接访问就是野指针。 - 二维数组内存分配逻辑完全错误:
char**是指针数组,每个元素指向一个独立的字符串内存块,你不能直接用*size * *str_size的大小分配整块内存。这种分配方式下data[data_count]读取的是内存块前几位的内容作为地址,本质是非法的野指针访问,是触发data[0][0]访问崩溃的直接原因。 - 扩容逻辑错误:数组元素个数不足时应该扩容指针数组,单个字符串长度不足时应该扩容对应下标的字符串内存,你当前所有扩容都直接重分配
data,计算逻辑完全错误,会直接破坏内存结构。 - fscanf返回值判断错误:
fscanf读取失败时返回EOF(值为-1),直接放在while判断条件中会被识别为真,导致文件读完后循环不会退出。
修正后的代码
/** * @brief 通过首个字段从CSV文件中读取指定行,动态分配内存给data数组 * * @param file 打开的CSV文件指针 * @param key 要匹配的行首字段 * @param data 出参,存储解析后的字符串数组,使用完需要调用者逐元素free后再free本身 * @param size 出参,存储返回的数组长度 * @param str_size 入参,指定每个字符串的初始分配大小,传0则使用默认值 * @return 0 成功,-1 未找到对应行,1 入参非法 */ int csv_get_row(FILE *file, char *key, char ***data, size_t *size, size_t *str_size) { if(!file || !key || !data || !size || !str_size) return 1; /* 获取匹配key的行起始偏移 */ long pos = find_key(file, key); if(pos == -1) return -1; fseek(file, pos, SEEK_SET); /* 初始化默认参数 */ if(*size < 1) *size = DEFAULT_ARRAY_SIZE; if(*str_size < 1) *str_size = DEFAULT_BUFFER_SIZE; /* 分配指针数组内存 */ *data = (char**) malloc(*size * sizeof(char*)); if(!*data) return 1; /* 初始化第一个字符串的内存 */ (*data)[0] = (char*) malloc(*str_size * sizeof(char)); if(!(*data)[0]) return 1; size_t data_count = 0; size_t str_pos = 0; char curr; /* 正确判断fscanf读取成功 */ while(fscanf(file, "%c", &curr) == 1) { /* 指针数组扩容 */ if(data_count >= *size) { *size *= 2; char** new_data = (char**) realloc(*data, *size * sizeof(char*)); if(!new_data) return 1; *data = new_data; } /* 当前字符串扩容 */ if(str_pos >= *str_size) { *str_size *= 2; char* new_str = (char*) realloc((*data)[data_count], *str_size * sizeof(char)); if(!new_str) return 1; (*data)[data_count] = new_str; } if(curr == ',') { (*data)[data_count][str_pos] = '\0'; data_count++; str_pos = 0; /* 为下一个字符串预分配内存 */ (*data)[data_count] = (char*) malloc(*str_size * sizeof(char)); if(!(*data)[data_count]) return 1; continue; } if(curr == '\n') { (*data)[data_count][str_pos] = '\0'; data_count++; break; } (*data)[data_count][str_pos] = curr; str_pos++; } /* 裁剪数组到实际长度 */ *size = data_count; char** new_data = (char**) realloc(*data, *size * sizeof(char*)); if(new_data) *data = new_data; return 0; }
调用注意事项
调用该函数时需要传入char**变量的地址,示例如下:
char** data = NULL; size_t size = 0; size_t str_size = 0; int ret = csv_get_row(file, "target_key", &data, &size, &str_size); // 使用完data后需要释放内存 for(size_t i=0; i<size; i++) { free(data[i]); } free(data);
内容的提问来源于stack exchange,提问作者Thomas Beatty
相关产品推荐
相关产品推荐

