【问题标题】:strtok() behavior is different across compilersstrtok() 行为因编译器而异
【发布时间】:2025-12-21 23:20:07
【问题描述】:

我写了一个程序,它根据这种格式解析一个字符串:

somethingsomething:number:

在我的电脑上,这个程序可以完美运行。 但是,一旦我上传了代码并在学校的计算机上编译了它,strtok() 就会有不同的行为。

例如,使用此字符串:p2test/f4.txt:1:,在我的计算机上,第一个令牌将是 p2test/f4.txt。但是,在学校的计算机上,令牌最终为 p2test/f4.t

这里是代码段:

            char *token;
            char delim[1] = ":";

            if ((token = strtok(tmp_string, delim)) != NULL) {
                ...
            }

在这里,tmp_string 将是 p2test/f4.txt:1:

这里是我电脑的编译器版本:gcc version 4.9.1 (Ubuntu 4.9.1-16ubuntu6)

这里是我学校的编译器版本:gcc version 4.8.1 20130909 [gcc-4_8-branch revision 202388](SUSE Linux)

【问题讨论】:

  • 未定义的行为因计算机而异,请发布其余代码,tmp_string 是如何生成的。
  • char delim[1] = ":"; 这个编译好了吗?该字符串文字中有两个字符,但您声明了一个 1 个字符的数组。另一个字符是空终止符。
  • @PaulMcKenzie 哦,你是对的!这很奇怪,因为我也打开了所有警告和迂腐。
  • @PaulMcKenzie:这是合法的 C(但不是 C++)。
  • @PaulMcKenzie 我完全错过了......

标签: c strtok


【解决方案1】:

另一个问题是这里有内存覆盖:

char delim[1] = ":";

字符串中有两个字符,而不是 1。这样做更安全:

char delim[] = ":";

【讨论】:

  • char delim[1] = ":";delim 初始化为一个字符,并不意味着有以下'\0'。使用char delim[] = ":"; 并不“更安全”,它或char delim[2] = ":"; 是算法所期望的。
【解决方案2】:

除了 cmets 中提到的几个问题外,代码似乎存在更大的问题。根据手册页中getline()函数的描述:

  If  *lineptr  is set to NULL and *n is set 0 before the call, then get‐
  line() will allocate a buffer for storing the line.  This buffer should
  be freed by the user program even if getline() failed.

  Alternatively, before calling getline(), *lineptr can contain a pointer
  to a malloc(3)-allocated buffer *n bytes in size.  If the buffer is not
  large  enough  to  hold the line, getline() resizes it with realloc(3),
  updating *lineptr and *n as necessary.

您没有初始化 tmp_string,因此它的值是一些指向某个随机地址的未初始化垃圾,并且发生了上述 getline() 的第二种替代行为。

这在我看来像是未定义的行为。

附:您确实将长度值初始化为 0 字节,根据 getline() 的手册页,这会导致它调用 realloc。因此,在我看来,您的代码最终会间接地将垃圾指针传递给 realloc。

【讨论】:

  • 我实际上已经在我在 gdb 中调试时编写的另一个函数中注意到了这一点。那么将来将所有未初始化的指针设置为NULL是否更安全?