strcat 如何影响 strtok?

How does strcat affect the strtok?

假设我们需要通过连接输入的标记将用户的输入复制到另一个字符串中,例如 "hello world" -> "helloworld".

#include <stdio.h>
#include <string.h>

int main(void) {
  char buffer[50];

  printf("\nEnter a string: ");

  while (fgets(buffer, sizeof(buffer), stdin) != 0) {
    size_t size = strlen(buffer);

    if (size > 0 && buffer[size - 1] == '\n') {
      char input[1]; // set it too small 
      buffer[size - 1] = '[=10=]';

      char *tok = strtok(buffer, " "); // works fine
      do {
        strcat(input, tok); // append to "input" that has not enough space
        printf("\nfound token: %s", tok);
        tok = strtok(NULL, " "); // produces garbage
      } while (tok);

     break;
  }
}

运行 上面的代码:

Enter a string: hello world

found token: hello
found token: w
found token: r
*** stack smashing detected ***: <unknown> terminated

我很难理解 strtokstrcat 未能追加 tok 有什么关系。他们不共享变量,除了 tok 是(根据文档)由 strcat 复制的,所以无论 strcat 做什么都不应该影响 strtok 行为和程序至少应该在第二次 strcat 调用时崩溃,对吧?但是我们看到 strcat 在检测到堆栈粉碎之前被调用了 3 次。你能解释一下为什么吗?

对于初学者这个数组

char input[1];

未初始化且不包含字符串。

所以调用strcat

strcat(input, tok);

调用未定义的行为也是因为数组输入不够大,无法存储复制的字符串。它可以覆盖数组之外的内存。

代码中存在多个问题:

  • char input[1];太小了,做不了什么。您不能将行中的标记连接到这个极小的数组中。您必须为其定义足够的长度,为了简单起见,即与 buffer 相同的长度。
  • input 必须初始化为空字符串,strcat(input, tok); 才能具有已定义的行为。如编码所示,对 strcat 的第一次调用会破坏导致观察到的行为的其他变量,但请注意,由于这种未定义的行为,其他任何事情都可能发生。
  • char *tok = strtok(buffer, " "); 工作正常,但如果 buffer 只包含空白,则可能 return 一个空指针。 do 循环将在 strcat(input, tok) 上调用未定义的行为。请改用 forwhile 循环。
  • 代码中缺少 },不清楚您的意思是在第一次迭代后从 while 循环中 break 还是仅在结束时行。

这是修改后的版本:

#include <stdio.h>
#include <string.h>

int main(void) {
    char buffer[50];
    char input[sizeof buffer] = "";

    printf("Enter a string: ");

    if (fgets(buffer, sizeof(buffer), stdin)) {
        char *tok = strtok(buffer, " \n");
        while (tok) {
            strcat(input, tok);
            printf("found token: %s\n", tok);
            tok = strtok(NULL, " \n");
        }
        printf("token string: %s\n", input);
    }
    return 0;
}