使用strtok_r时出现分段错误

18

有人能解释一下为什么我在以下示例中会遇到分段错误吗?

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

int main(void) {
  char *hello = "Hello World, Let me live.";
  char *tokens[50];
  strtok_r(hello, " ,", tokens);
  int i = 0;
  while(i < 5) {
    printf("%s\n", tokens[i++]);
  }
}
6个回答

25

试一下这个:

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

int main(void) {
        char hello[] = "Hello World, Let me live."; // make this a char array not a pointer to literal.
        char *rest; // to point to the rest of the string after token extraction.
        char *token; // to point to the actual token returned.
        char *ptr = hello; // make q point to start of hello.

        // loop till strtok_r returns NULL.
        while(token = strtok_r(ptr, " ,", &rest)) {

                printf("%s\n", token); // print the token returned.
                ptr = rest; // rest contains the left over part..assign it to ptr...and start tokenizing again.    
        }
}
/*
Output:
Hello
World
Let
me
live.
*/

这个例子在printf行上给我一个segfault。gdb打印token显示0xffffffffffffdad0 <Address 0xffffffffffffdad0 out of bounds> 当我编译proj1.c时还会得到这两个警告:33:2:警告:隐式声明函数'strtok_r'[-Wimplicit-function-declaration] proj1.c:33:14:警告:赋值使指针从整数转换而来,没有强制转换[默认启用] - Nathan Schwermann
1
抱歉我是新手,为什么不应该是 char *ptr = Hello; 大写H?另外,Alok的回答说第一次调用需要 "标记化" 第一个参数,然后后续调用它需要为NULL,但是你的示例似乎只在while循环中调用了一种方式?顺便感谢你提供的代码 - SSH This
*ptr = hello; 是因为 hello 指向 char hello[]... - MrHIDEn

17
  • 需要在循环中调用strtok_r。第一次给它要被分词的字符串,然后将NULL作为第一个参数传递。
  • strtok_r使用char **作为第三个参数。变量tokens是一个包含50个char *值的数组。当您将tokens传递给strtok_r()时,传递的是一个指向该数组第一个元素的char **值。这是可以的,但您浪费了其余49个完全未使用的值。您应该有一个char *last;变量,并将&last作为strtok_r()的第三个参数。
  • strtok_r()会修改其第一个参数,因此无法传递不能修改的内容。在C语言中,字符串常量是只读的,因此您需要使用可修改的内容:char hello[] = "Hello World, Let me live.";

1
谢谢您的回答。我希望 Stack Overflow 允许将多个答案标记为正确。 :) - Phulore R - Profile 2
@Scrub:很高兴能帮到你。请确保你理解了我上面的第二点(关于char *tokens[50];在传递给函数时等同于char **)。 - Alok Singhal
1
救了我;+1 提醒我,参数 1 不能是 const char *。对于其他人:不要从 .RODATA 部分输入数据。或者:尝试先将其复制到临时缓冲区中,看看是否有效。 - Joel

5

有几个问题:

  1. hello 指向一个字符串字面量,必须被视为不可变的。(它可能存在于只读内存中。)由于 strtok_r 改变其参数字符串,所以您不能将 hello 与它一起使用。

  2. 您只调用了一次 strtok_r,并且没有初始化您的 tokens 数组指向任何内容。

尝试这样做:

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

int main(void) {
  char hello[] = "Hello World, Let me live.";
  char *p = hello;
  char *tokens[50];
  int i = 0;

  while (i < 50) {
     tokens[i] = strtok_r(p, " ,", &p);
     if (tokens[i] == NULL) {
        break;
     }
     i++;
  }

  i = 0;
  while (i < 5) {
    printf("%s\n", tokens[i++]);
  }

  return 0;
}

谢谢你的回答。我希望 Stack Overflow 允许将多个答案标记为正确。 :) - Phulore R - Profile 2

3

strtok_r试图将null字符写入hello(这是非法的,因为它是一个const字符串)


我尝试了 char hello[50]。段错误已经消失了,但现在的问题是 printf 只打印出了悲伤的空白行。:( - Phulore R - Profile 2

2

您对strtok_r的使用理解不正确。请参考此示例和文档。

并且尝试查看以下内容:

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

int main(void)
{
    char hello[] = "Hello World, let me live.";

    char *tmp;
    char *token = NULL;
    for(token = strtok_r(hello, ", ", &tmp);
        token != NULL;
        token = strtok_r(NULL, ", ", &tmp))
    {
        printf("%s\n", token);
    }

    return 0;
}

0
我认为问题可能出在 char *tokens[50]; 这一行,因为你已经将它声明为指针了,而数组在声明时本身就是一个指针。你应该使用 char tokens[50]; 来代替。这样应该就可以解决问题了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接