char * strtok(char * s1,const char * s2)
重复调用此函数会将字符串s1分解为“标记” - 即 字符串被分成子串, 每个都以'\ 0'结尾,其中 '\ 0'替换任何字符 包含在字符串s2中。第一个电话 使用字符串标记为s1; 后续调用使用NULL作为第一个 论点。指向开头的指针 返回当前令牌;空值 如果没有,则返回 令牌。
嗨,
我刚才尝试使用strtok
并发现如果我将char*
传入s1
,我会遇到分段错误。如果我传入char[]
,strtok
工作正常。
为什么会这样?
我用Google搜索,其原因似乎是char*
只读,char[]
是可写的。非常感谢更全面的解释。
答案 0 :(得分:14)
您将char *
初始化为什么?
如果像
那样的话char *text = "foobar";
然后你有一个指向一些只读字符的指针
对于
char text[7] = "foobar";
然后你有一个七元素的字符数组,你可以做你喜欢的。
strtok
写入你给它的字符串 - 用null
覆盖分隔符,并保持指向字符串其余部分的指针。
因此,如果你传递一个只读字符串,它会尝试写入它,你会得到一个段错误。
此外,因为strtok
保留对字符串其余部分的引用,它不是reeentrant - 您一次只能在一个字符串上使用它。最好避免,真的 - 考虑strsep(3) - 例如,请参见:http://www.rt.com/man/strsep.3.html(尽管仍然写入字符串,因此具有相同的只读/段错误问题)
答案 1 :(得分:5)
推断但未明确说明的重点:
基于你的问题,我猜你在C语言编程方面相当新,所以我想更多地解释一下你的情况。如果我弄错了,请原谅我; C可能很难学习,主要是因为底层机制存在细微的误解,所以我喜欢尽可能简单。
如您所知,当您编写C程序时,编译器会根据语法为您预先创建所有内容。在代码中的任何位置声明变量时,例如:
int x = 0;
编译器读取这行文本并对自己说:好的,我需要用x
的当前代码范围中的所有实例替换为我分配用于保存的内存区域的常量引用整数。
当您的程序运行时,此行会导致新的操作:我需要设置x
引用int
值0
的内存区域。
请注意这里的细微差别:参考点x
所持有的内存位置是不变的(并且无法更改)。但是,x
指向的值可以更改。您可以通过分配在代码中执行此操作,例如: x = 15;
。另请注意,单行代码实际上相当于编译器的两个单独命令。
如果您有以下声明:
char *name = "Tom";
编译器的过程是这样的:好的,我需要用name
的当前代码范围中的所有出现次数替换为我已分配用于保存char
的内存区域的常量引用指针值。它就是这样。
但是还有第二步,相当于:我需要创建一个常量字符数组,其中包含值'T','o','m'和NULL
。然后我需要将代码的"Tom"
部分替换为该常量字符串的内存地址。
当你的程序运行时,最后一步是:将指针设置为char
的值(不是常数)到自动创建的字符串的内存地址(是常数)。
因此char *
不是只读的。只有const char *
是只读的。但是在这种情况下你的问题不是char *
是只读的,而是你的指针引用了只读内存区域。
我提出这一切是因为理解这个问题是你从图书馆查看该功能定义与自己理解问题而不是问我们之间的障碍。我有点简化了一些细节,希望能让这个问题更容易理解。
我希望这很有帮助。 ;)
答案 2 :(得分:2)
我责怪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中]
答案 3 :(得分:0)
简而言之:
char *s = "HAPPY DAY";
printf("\n %s ", s);
s = "NEW YEAR"; /* Valid */
printf("\n %s ", s);
s[0] = 'c'; /* Invalid */
答案 4 :(得分:0)
如果查看编译器文档,可能会设置一个选项来使这些字符串可写。