strtok分段错误

我试图理解为什么下面的代码片段给出了分段错误:

void tokenize(char* line) { char* cmd = strtok(line," "); while (cmd != NULL) { printf ("%s\n",cmd); cmd = strtok(NULL, " "); } } int main(void) { tokenize("this is a test"); } 

我知道strtok()实际上并没有对字符串文字进行标记,但在这种情况下, line直接指向字符串"this is a test" ,它在内部是一个char数组。 是否有任何令牌化line而不将其复制到数组中?

问题是您正在尝试修改字符串文字。 这样做会导致程序的行为未定义。

说你不允许修改字符串文字是过于简单化了。 说字符串文字是const是不正确的; 他们不是。

警告:接下来是。

字符串文字"this is a test"char[15]类型的表达式(长度为14,终止'\0' )。 在大多数情况下,包括这个表达式,这样的表达式被隐式转换为指向数组的第一个元素的指针,类型为char*

尝试修改由字符串文字引用的数组的行为是未定义的 – 不是因为它是const (它不是),而是因为C标准明确指出它是未定义的。

有些编译器可能允许你逃避这一点。 您的代码可能实际上修改了与文字对应的静态数组(这可能会在以后引起很大的混淆)。

但是,大多数现代编译器都会将数组存储在只读存储器中 – 而不是物理ROM,但存储在受虚拟存储器系统修改的内存区域中。 尝试修改此类内存的结果通常是分段错误和程序崩溃。

那么为什么字符串文字不是 const ? 既然你真的不应该尝试修改它们,那肯定会有意义 – 而C ++确实使字符串文字成为const 。 原因是历史性的。 const关键字在1989 ANSI C标准引入之前不存在(虽然它可能在之前由一些编译器实现)。 因此,ANSI之前的程序可能如下所示:

 #include  print_string(s) char *s; { printf("%s\n", s); } main() { print_string("Hello, world"); } 

没有办法强制执行不允许print_string修改s指向的字符串这一事实。 在ANSI C中创建字符串文字const将破坏现有代码,ANSI C委员会非常努力避免这样做。 从那以后,没有很好的机会对语言进行这样的改变。 (C ++的设计者,主要是Bjarne Stroustrup,并不关心与C的向后兼容性。)

正如您所说,您无法修改字符串文字,这正是strtok所做的。 你必须做

 char str[] = "this is a test"; tokenize(str); 

这将创建数组str并初始化它, this is a test\0 ,并将指针传递给它以进行tokenize

尝试标记编译时常量字符串会导致分段错误是一个很好的理由:常量字符串在只读内存中。

C编译器将编译时常量字符串加到可执行文件中,操作系统将它们加载到只读内存(* nix ELF文件中的.rodata)。 由于此内存被标记为只读,并且由于strtok写入您传入其中的字符串,因此写入只读内存会出现分段错误。

我相信你会被殴打……但是“strtok()”本质上是不安全的,容易发生访问违规等问题。

在这里,答案几乎肯定是使用字符串常量。

试试这个:

 void tokenize(char* line) { char* cmd = strtok(line," "); while (cmd != NULL) { printf ("%s\n",cmd); cmd = strtok(NULL, " "); } } int main(void) { char buff[80]; strcpy (buff, "this is a test"); tokenize(buff); } 

Strok修改其第一个参数以便对其进行标记。 因此,您不能将它传递给文字字符串,因为它的类型为const char *且无法修改,因此未定义的行为。 您必须将字符串文字复制到可以修改的char数组中。

你想通过“……内部是一系列的char这句话来做什么?

"this is a test"事实在内部是一个char数组,根本不会改变任何东西。 它仍然是一个字符串文字(所有字符串文字都是不可修改的char数组)。 你的strtok仍然试图标记一个字符串文字。 这就是崩溃的原因。

我只是在它变为NULL之后尝试使用printf打印令牌(在你的情况下为cmd )时遇到了Segmentation Fault错误。