C语言中检查char*是否在LabelList的函数异常返回0的排查与解决
CSV标签数据加载问题排查与解决
问题背景
我的程序需要从CSV文件加载数据,每行最后一段是对应数据点的标签。由于标签会重复,我采用存储标签列表、让程序内数据存储标签索引的方式提升效率。标签列表的初始实现如下(后续会补充temp == NULL的错误处理逻辑):
typedef struct _LabelList { size_t size; char **label_table; } LabelList; void add_label(LabelList *list, char *label) { if (list->label_table == NULL) { list->size = 1; char **temp = malloc(sizeof(char *)); if (temp != NULL) { list->label_table = temp; list->label_table[0] = label; } return; } else { list->size++; size_t temp_size = list->size; char **temp = realloc(list->label_table, temp_size * sizeof(char *)); if (temp != NULL) { list->label_table = temp; list->label_table[temp_size-1] = label; } return; } }
问题现象
check_if_exists函数单独测试时运行正常:若传入的char*在列表中则返回索引,否则返回-1,但被其他函数调用时出现异常。具体表现为:第一个标签能正常添加到label_list,但后续调用create_labeled_data_point时,check_if_exists始终返回0——排查发现该函数内label_list的第一个元素始终与to_check参数相同,导致strcmp每次都返回0。最初用链表实现时也遇到了同样的问题。
相关问题函数代码如下:
int check_if_exists(LabelList list, char *to_check) { size_t length = list.size; int exists; for (size_t i = 0; i < length; i++) { exists = strcmp(list.label_table[i], to_check); if (exists == 0) return i; } return -1; } LabeledDataPoint *create_labeled_data_point(char *data, size_t rec_count, LabelList *label_list) { char *token; char *end; LabeledDataPoint *point = malloc(sizeof(size_t) + rec_count*sizeof(double) + sizeof(char *)); point->dims = rec_count-1; token = strtok(data, ","); point->data[0] = atof(token); for (size_t i = 1; i < rec_count-1; i++) { token = strtok(NULL, ","); point->data[i] = atof(token); }; token = strtok(NULL, ","); int index_in_list = check_if_exists(label_list, token); if (index_in_list == -1) { add_label(label_list, token); point->label_num = label_list->size-1; } else { point->label_num = index_in_list; } return point; } LabeledDataPoint **load_labeled_data_csv(FILE *file, size_t num_records, size_t num_fields, LabelList *label_list) { LabeledDataPoint **all_data = malloc(num_records*(sizeof(size_t) + num_fields*sizeof(double))); LabeledDataPoint *current_data_point; char *current_record = NULL; size_t n = 0; int chars_num; for (size_t i = 0; i < num_records; i++) { chars_num = getline(¤t_record, &n, file); if (chars_num != -1) { current_data_point = create_labeled_data_point(current_record, num_fields, label_list); } all_data[i] = current_data_point; } free(current_record); return all_data; }
解决方法
经过调整,修改了两个核心函数,同时新增内存释放函数避免内存泄漏:
修改后的create_labeled_data_point函数
LabeledDataPoint *create_labeled_data_point(char *data, size_t rec_count, LabelList *label_list) { char *token; char *end; LabeledDataPoint *point = malloc(sizeof(size_t) + rec_count*sizeof(double) + sizeof(char *)); point->dims = rec_count-1; token = strtok(data, ","); point->data[0] = atof(token); for (size_t i = 1; i < rec_count-1; i++) { token = strtok(NULL, ","); point->data[i] = atof(token); }; token = strtok(NULL, ","); // 复制最后一个token,原data指针可安全释放 char *token_copy = malloc(strlen(token)+1); strcpy(token_copy, token); int index_in_list = check_if_exists(*label_list, token); // 标签已存在则释放复制的token,否则保留token待列表废弃时统一释放 if (index_in_list == -1) { add_label(label_list, token_copy); point->label_num = label_list->size-1; } else { point->label_num = index_in_list; free(token_copy); } return point; }
修改后的load_labeled_data_csv函数
LabeledDataPoint **load_labeled_data_csv(FILE *file, size_t num_records, size_t num_fields, LabelList *label_list) { LabeledDataPoint **all_data = malloc(num_records*(sizeof(size_t) + num_fields*sizeof(double))); LabeledDataPoint *current_data_point; char *current_record = NULL; size_t n = 0; int chars_num; for (size_t i = 0; i < num_records; i++) { chars_num = getline(¤t_record, &n, file); if (chars_num != -1) { current_data_point = create_labeled_data_point(current_record, num_fields, label_list); } all_data[i] = current_data_point; // 每次循环后释放current_record并重置为NULL free(current_record); current_record = NULL; n = 0; } return all_data; }
新增内存释放函数free_label_list
void free_label_list(LabelList *list) { size_t size = list->size; for (size_t i = 0; i < size; i++) { free(list->label_table[i]); } free(list->label_table); }
内容的提问来源于stack exchange,提问作者glacierDexeryl
相关产品推荐
相关产品推荐

