字符串转结构体:汽车信息解析函数无法正确提取车型名称问题排查
问题排查:strtok解析汽车名称异常的原因
我有一个存储汽车信息的文件,每行格式为:
car_1_name [car_1_id] & car_2_name [car_2_id]
我希望将每辆汽车的名称和ID存入数组,为此定义了_Car_和_Car_List_两个结构体。但编写的用于解析该格式行的parse函数(使用strtok分割字符串)无法正确提取汽车名称,不过手动向列表添加汽车时字符串显示正常。以下是最小可复现代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> struct _Car_ { char* name_; int id_; struct _Car_* parent_1_; struct _Car_* parent_2_; }; struct _Car_List_ { struct _Car_* car_list[100]; int number_of_cars; }; struct _Car_* createCar(char* name, int id) { struct _Car_* car = malloc(sizeof(struct _Car_)); if (car == NULL) return NULL; car -> name_ = name; car -> id_ = id; return car; } void addCar(struct _Car_List_* my_list, struct _Car_* car) { my_list -> car_list[my_list -> number_of_cars] = car; my_list -> number_of_cars = my_list -> number_of_cars + 1; } int parse(struct _Car_List_* my_list) { char string[] = "Ferrari [3] & Porsche [6]\n"; char* car_1_string = strtok(string, "&"); char* car_2_string = strtok(NULL, "&"); char* car_1_name = strtok(car_1_string, "["); int car_1_id = atoi(strtok(NULL, "]")); char* car_2_name = strtok(car_2_string, "["); int car_2_id = atoi(strtok(NULL, "]")); struct _Car_* car_1 = createCar(car_1_name, car_1_id); struct _Car_* car_2 = createCar(car_2_name, car_2_id); addCar(my_list, car_1); addCar(my_list, car_2); return 0; } void printList(struct _Car_List_* my_list) { int i = 0; struct _Car_* car = my_list -> car_list[i]; while (car != NULL) { printf("- %s [%d]\n", car -> name_, car -> id_); i = i + 1; car = my_list -> car_list[i]; } } int testAdd(struct _Car_List_* my_list) { struct _Car_* car = createCar("Renault", 1); addCar(my_list, car); return 0; } int main() { struct _Car_List_* my_list = malloc(sizeof(struct _Car_List_)); my_list -> number_of_cars = 0; for (int i = 0; i < 100; i = i + 1) my_list -> car_list[i] = NULL; testAdd(my_list); parse(my_list); printf("Below the printed list:\n"); printList(my_list); return 0; }
问题原因及修复方案
1. 名称包含多余空格
用strtok(car_1_string, "[")分割后,car_1_name指向的是"Ferrari "(末尾带空格),car_2_name是" Porsche "(首尾都有空格),打印时会显示多余空格,看起来像是解析异常。
2. 局部字符串生命周期问题
parse函数中的char string[]是栈上的局部变量,函数执行完毕后该内存会被系统收回。但createCar直接把name指针指向这个局部变量的地址,后续访问car->name_时会触发未定义行为(可能打印乱码、程序崩溃)。而手动添加的"Renault"是字符串常量,生命周期覆盖整个程序,所以能正常显示。
修复步骤
步骤1:修剪名称中的空格
添加一个修剪字符串首尾空格的工具函数:
char* trim(char* str) { // 跳过开头所有空格 while (*str == ' ') str++; if (*str == '\0') return str; // 定位末尾空格位置 char* end = str + strlen(str) - 1; while (end > str && *end == ' ') end--; // 添加字符串结束符 *(end + 1) = '\0'; return str; }
提取名称后调用该函数:
char* car_1_name = strtok(car_1_string, "["); trim(car_1_name); int car_1_id = atoi(strtok(NULL, "]")); char* car_2_name = strtok(car_2_string, "["); trim(car_2_name); int car_2_id = atoi(strtok(NULL, "]"));
步骤2:为名称分配堆内存
修改createCar函数,为name_单独分配堆内存并复制字符串,避免依赖局部变量:
struct _Car_* createCar(char* name, int id) { struct _Car_* car = malloc(sizeof(struct _Car_)); if (car == NULL) return NULL; // 为名称分配内存并复制内容 car->name_ = malloc(strlen(name) + 1); if (car->name_ == NULL) { free(car); return NULL; } strcpy(car->name_, name); car->id_ = id; car->parent_1_ = NULL; // 初始化未使用的指针,避免野指针 car->parent_2_ = NULL; return car; }
步骤3:(可选)添加内存释放逻辑
为避免内存泄漏,添加释放整个汽车列表的函数:
void freeCarList(struct _Car_List_* my_list) { for (int i = 0; i < my_list->number_of_cars; i++) { free(my_list->car_list[i]->name_); free(my_list->car_list[i]); } free(my_list); }
在main函数末尾调用freeCarList(my_list);即可。
内容的提问来源于stack exchange,提问作者3nondatur
相关产品推荐
相关产品推荐

