C的strtok()和只读字符串文字

char * strtok(char * s1,const char * s2)

重复调用此函数将字符串s1分解为“标记” – 即字符串被分解为子字符串,每个子字符串以’\ 0’结尾,其中’\ 0’替换字符串s2中包含的任何字符。 第一个调用使用字符串标记为s1; 后续调用使用NULL作为第一个参数。 返回指向当前标记开头的指针; 如果没有更多令牌,则返回NULL。

嗨,

我一直在尝试使用strtok ,并发现如果我将char*传入s1 ,我会遇到分段错误。 如果我传入char[]strtok工作正常。

为什么是这样?

我google了一下,原因似乎是char*如何只读, char[]是可写的。 将非常感谢更彻底的解释。

你把char *初始化了什么?

如果有的话

 char *text = "foobar"; 

然后你有一个指向一些只读字符的指针

对于

 char text[7] = "foobar"; 

然后你有一个七元素的字符数组,你可以做你喜欢的。

strtok写入你给它的字符串 – 用null覆盖分隔符,并保持指向字符串其余部分的指针。

因此,如果您传递一个只读字符串,它将尝试写入它,并获得段错误。

此外,因为strtok保持对字符串其余部分的引用,它不是reeentrant – 您一次只能在一个字符串上使用它。 最好避免,真的 – 考虑strsep(3) – 例如,请参阅: http ://www.rt.com/man/strsep.3.html(尽管仍写入字符串,因此具有相同的读取-only / segfault问题)

推断但未明确说明的重点:

基于你的问题,我猜你在C语言编程方面相当新,所以我想更多地解释一下你的情况。 如果我弄错了,请原谅我; C很难学习,主要是因为底层机制存在细微的误解,所以我喜欢尽可能简单。

如您所知,当您编写C程序时,编译器会根据语法为您预先创建所有内容。 在代码中的任何位置声明变量时,例如:

int x = 0;

编译器读取这行文本并对自己说:好的,我需要将x的当前代码范围中的所有实例替换为对已分配用于保存整数的内存区域的常量引用。

当你的程序运行时,这一行会导致一个新动作:我需要设置x引用int0的内存区域。

请注意这里的细微差别:参考点x保持的存储位置是恒定的(并且不能更改)。 但是, x点的值可以更改。 您可以通过赋值在代码中执行此操作,例如x = 15; 。 另请注意,单行代码实际上相当于编译器的两个单独命令。

如果您有以下声明:

char *name = "Tom";

编译器的过程是这样的:好的,我需要将当前代码范围中的所有实例替换为对已分配用于保存char指针值的内存区域的常量引用。 它就是这样。

但是还有第二步,相当于:我需要创建一个常量字符数组,其中包含值’T’,’o’,’m’和NULL 。 然后我需要用"Tom"常量字符串"Tom"的内存地址替换代码"Tom"的部分。

运行程序时,最后一步是:将指针设置为char的值(不是常量)到自动创建的字符串的内存地址(这常量)。

所以char *不是只读的。 只有const char *是只读的。 但是在这种情况下你的问题不是char * s是只读的,而是你的指针引用了只读内存区域。

我提出这一切是因为理解这个问题是你从图书馆看这个function的定义和自己理解问题而不是问我们之间的障碍。 我有点简化了一些细节,希望能让这个问题更容易理解。

我希望这可以帮到你。 ;)

我责怪C标准。

 char *s = "abc"; 

可能已被定义为给出相同的错误

 const char *cs = "abc"; char *s = cs; 

基于字符串文字是不可修改的。 但事实并非如此,它被定义为编译。 去搞清楚。 [编辑:Mike B已经想到了 – “const”根本不存在于K&R C. ISO C,加上每个版本的C和C ++,因为它想要向后兼容。 所以它必须是有效的。]

如果它被定义为给出错误,那么你就不可能达到段错误,因为strtok的第一个参数是char *,因此编译器会阻止你传入从文字生成的指针。

可能有趣的是,C ++中的计划曾一度被弃用( http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1996/N0896.asc )。 但12年后,我无法说服gcc或g ++给我任何警告,将文字分配给非const char *,所以并非所有人都大声弃用。

[编辑:aha:-Wwrite-strings,未包含在-Wall或-Wextra中]

简单来说:

 char *s = "HAPPY DAY"; printf("\n %s ", s); s = "NEW YEAR"; /* Valid */ printf("\n %s ", s); s[0] = 'c'; /* Invalid */ 

如果查看编译器文档,可能会设置一个选项来使这些字符串可写。