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

Arduino字符串分割程序输出异常:strtok分割结果不符合预期

异常根因定位

原代码存在两个核心逻辑错误,直接导致输出异常:

  • 类型不匹配导致逐字符读取:stringsplit函数当前返回值为*words,等价于返回words[0]——也就是第一个分割字符串的起始字符指针(char*类型)。后续代码把这个指向单个字符串的指针,直接当成字符数组逐位取值:p1[0]是第一个字符串的首字符、p1[1]是第一个字符串的第二个字符,最终只能输出单字符,完全匹配你看到的f、i、r、s的输出结果。
  • 局部栈内存的野指针风险:函数内部定义的char *words[4]是运行在栈上的局部数组,函数一旦执行完毕,栈帧被回收,就算修正返回值逻辑,后续读取数组内容也会出现数据错乱、乱码的问题。
修复方案

修改核心逻辑:将存储分割结果的指针数组改为静态存储、修正返回值类型匹配关系,最终可用代码如下:

// 返回值为二级指针,指向存储分割结果的指针数组首地址
char** stringsplit(char msg[]) {
  // 用static修饰数组,存储在静态内存区,不会随函数退出被回收
  static char *words[4];
  char *p;
  int i = 0;
  char dauphay[] = ",";
  p = strtok(msg, dauphay);
  while(p && i < 4) {
    words[i] = p;
    p = strtok(NULL, dauphay);
    i++;
  }
  // 未填满的数组位置设置为空指针,避免后续读取到无效值
  while(i < 4) {
    words[i] = NULL;
    i++;
  }
  // 直接返回数组首地址,不做解引用操作
  return words;
}

void setup() {
  Serial.begin(115200);
  char msg[] = "first,Second,third,four";
  // 用二级指针接收返回的指针数组地址
  char **p1 = stringsplit(msg);
  for(int i = 0; i < 4; i++) {
    // 遇到空指针就终止循环,避免读取乱码
    if(p1[i] == NULL) break;
    Serial.println(p1[i]);
  }
}

void loop() {
}
代码优化说明
  • 给内部存储分割结果的数组加static修饰后,数组内存和程序生命周期一致,彻底解决局部栈内存回收导致的野指针问题
  • 修正返回值类型为char**,和调用方的接收变量类型匹配,此时p1[i]对应每个分割字符串的起始地址,传给Serial.println即可输出完整的分割结果
  • 增加空值判断逻辑,当输入字符串分割后的段数不足4个时,会直接终止输出,不会读到内存中的随机乱码
  • 注意:strtok会直接修改传入的原字符串,将分隔符替换为字符串结束符,因此传入的参数必须是可修改的字符数组,不能直接传入字符串常量(存在于Flash只读区,修改会触发硬件异常)

内容的提问来源于stack exchange,提问作者nghihoang2207

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.26 04:15:00