我试图理解为什么以下代码片段会产生分段错误:
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
而没有将其复制到数组中?
答案 0 :(得分:20)
问题是您正在尝试修改字符串文字。这样做会导致程序的行为未定义。
说你不允许修改字符串文字是过于简单化了。说字符串文字是const
是不正确的;他们不是。
警告:继续进行挖掘。
字符串文字"this is a test"
是char[15]
类型的表达式(长度为14,终止'\0'
为1)。在大多数情况下,包括这个表达式,这样的表达式被隐式转换为指向类型char*
的第一个元素的指针。
尝试修改字符串文字所引用的数组的行为是未定义的 - 不是因为它是const
(它不是),而是因为C标准明确指出它是未定义的。
有些编译器可能会允许您逃避这一点。您的代码可能实际上修改了与文字对应的静态数组(这可能会在以后引起很大的混淆)。
但是,大多数现代编译器都会将数组存储在只读存储器中 - 而不是物理ROM,而是存储在受虚拟内存系统修改的内存区域中。尝试修改此类内存的结果通常是分段错误和程序崩溃。
为什么不是字符串文字const
?既然你真的不应该尝试修改它们,那肯定会有意义 - 而C ++确实会创建字符串文字const
。原因是历史性的。 const
关键字在1989 ANSI C标准引入之前不存在(尽管之前可能由某些编译器实现)。因此,ANSI之前的程序可能如下所示:
#include <stdio.h>
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的向后兼容性。)
答案 1 :(得分:2)
正如您所说,您无法修改字符串文字,这是strtok
的作用。你必须这样做
char str[] = "this is a test";
tokenize(str);
这将创建数组str
并使用this is a test\0
对其进行初始化,并将指针传递给tokenize
。
答案 2 :(得分:2)
有一个很好的理由,试图标记化编译时常量字符串会导致分段错误:常量字符串在只读内存中。
C编译器将编译时常量字符串加到可执行文件中,操作系统将它们加载到只读内存(* nix ELF文件中的.rodata)。由于此内存标记为只读,并且由于strtok写入您传入其中的字符串,因此写入只读内存时会出现分段错误。
答案 3 :(得分:1)
我相信你会被殴打......但是“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);
}
答案 4 :(得分:1)
Strok修改其第一个参数以便对其进行标记。因此,您不能将它传递给文字字符串,因为它的类型为const char *
且无法修改,因此未定义的行为。您必须将字符串文字复制到可以修改的char数组中。
答案 5 :(得分:1)
您想通过“创建的内容...... ......内部是char
”的数组?
"this is a test"
内部是char
数组的事实根本不会改变任何内容。它仍然是一个字符串文字(所有字符串文字都是不可修改的char数组)。您的strtok
仍尝试对字符串文字进行标记。这就是崩溃的原因。
答案 6 :(得分:0)
我只是在尝试使用printf在它变为NULL后打印令牌(cmd
)时遇到了Segmentation Fault错误。