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错误。