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

字符串转结构体:汽车信息解析函数无法正确提取车型名称问题排查

问题排查: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 04:50:23