strtok - 字符数组与字符指针的区别

15

可能是重复问题:
strtok不接受:char *str

当使用strtok函数时,使用char *而不是char []会导致分段错误。

以下代码可以正常运行:

char string[] = "hello world";
char *result = strtok(string, " ");

这会导致分段错误:

char *string = "hello world";
char *result = strtok(string, " ");

有人能解释一下是什么原因导致了这种行为上的差异吗?

6个回答

35
char string[] = "hello world";

这行代码初始化了一个足够大的字符数组 string (在本例中为 char[12])并将这些字符复制到您的本地数组中,就好像您手动输入了它们一样。

char string[] = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\0' };

另一行:

char* string = "hello world";

不会初始化一个本地数组,它只是初始化了一个本地指针。编译器可以将其设置为指向一个数组的指针,你不能改变它,就好像代码是这样的:

const char literal_string[] = "hello world";
char* string = (char*) literal_string;

C允许这样做的原因主要是为了让古老的代码继续编译。在源代码中,您应该将字符串字面量的类型视为const char[],它可以转换为const char*,但永远不要将其转换为char*


2
很多好的答案,但我发现这个是最清晰的基本问题示例。 - Elle H

16
在第二个例子中:
char *string = "hello world";
char *result = strtok(string, " ");

指针string指向一个字符串常量,这个常量是不可修改的(而strtok()需要修改它)。

你可以尝试以下方式:

char *string = strdup("hello world");
char *result = strtok(string, " ");

这样string指向了一个可修改的文字字面量的副本。


我要抑制自己的冲动,不打负分,但我真的不喜欢这个答案。我认为它会让新手程序员习惯于随意使用strdup来解决段错误,而不是学习如何管理内存(尤其是字符串)。但我不确定是否有更好的答案,而不是说“只需使用数组或动态分配内存来处理字符串”。顺便说一下,strdup不是标准C,但当然可以在没有它的系统上轻松实现。 - R.. GitHub STOP HELPING ICE

4

strtok函数会修改你传递给它的字符串(或者至少试图这样做)。在你的第一个代码中,你传递了一个已经初始化为特定值的数组的地址--但是由于它是一个普通的字符数组,所以允许修改它。

在第二个代码中,你传递了一个字符串字面量的地址。尝试修改一个字符串字面量会导致未定义的行为。


3
在第二种情况下(char *),字符串存储在只读内存中。正确的字符串常量类型是const char *,如果你使用这个类型声明变量,并尝试修改它,编译器会发出警告。由于历史原因,即使这些字符串不能被修改,你仍然可以使用字符串常量来初始化char *类型的变量。(一些编译器允许你关闭这个历史许可证,例如使用gcc的-Wwrite-strings选项。)

值得一提的是,在第一种情况下,字符串字面量会被隐式复制到字符数组中。这就是为什么你没有遇到同样的问题。 - Brian Clements
const char * 如果尝试与 strtok 一起使用,实际上也会导致段错误,但至少它会给出编译警告。但需要注意的是修改可能是问题的原因。 - Elle H
是的,我应该少用电报风格。回答已编辑。 - zwol
如果您将 const char * 传递给 strtok,应该会产生编译器错误而不是警告。C语言没有隐式转换可以删除限定符;您需要进行显式转换。 - R.. GitHub STOP HELPING ICE
"[construct] discards qualifiers from pointer target type" 在 gcc 4.2 中只是一个警告,即使加上 -pedantic。它似乎也不是针对 char * 的特殊情况。 - zwol

0

第一种情况创建了一个足够大的(非const)char数组,并用字符串内容初始化它。第二种情况创建了一个char指针,并将其初始化为指向字符串字面量,该字面量可能存储在只读内存中。

由于strtok想要修改您传递给它的参数所指向的内存,因此后一种情况会导致未定义的行为(您正在传递指向(const)字符串字面量的指针),因此很容易崩溃。


0
因为第二个声明了一个指向常量字符串的指针(可以更改)...
因此,根据您的编译器/平台/操作系统/内存映射...“hello world”字符串将被存储为常量(在嵌入式系统中,它可能存储在ROM中),尝试修改它将导致该错误。

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