使用字符指针作为strtok函数的参数

3
我尝试使用strtok函数来分割字符串。但是,如果我将字符指针作为此函数的参数,则程序会失败。
如果我将字符串初始化为s2s3,程序可以正常工作。但如果我使用字符指针s1,程序会出现Segmentation fault (core dumped)错误。
char *s1 = "1A 2B 3C 4D";
char s2[] = "1A 2B 3C 4D";
char s3[20] = "1A 2B 3C 4D";

问题在于其他函数,printfstrlen 没有问题,但是只有 strtok 函数出现错误。
下面是完整的代码:
#include <stdio.h>
#include <stdlib.h>
#include<string.h>

void split_string(char *s) {
    char * token = strtok(s," ");
    while (token != NULL) {
        printf("%s\n", token);
        token = strtok(NULL, " ");
    }
}

int main()
{
    char *s1 = "1A 2B 3C 4D";
    char s2[] = "1A 2B 3C 4D";
    char s3[20] = "1A 2B 3C 4D";
    printf("size of s1 = %ld, s2 = %ld, s3 = %ld\n", strlen(s1), strlen(s2), strlen(s3));
    printf("s1: %s\ns2: %s\ns3: %s\n",s1,s2,s3);
    printf("split s2: \n");
    split_string(s2);
    printf("split s3: \n");
    split_string(s3);
    printf("split s1: \n");
    split_string(s1);
    return 0;
}

运行后的结果:

size of s1 = 11, s2 = 11, s3 = 11
s1: 1A 2B 3C 4D
s2: 1A 2B 3C 4D
s3: 1A 2B 3C 4D
split s2: 
1A
2B
3C
4D
split s3: 
1A
2B
3C
4D
split s1: 
Segmentation fault (core dumped)

man页面上的strtok函数: char *strtok(char *str, const char *delim);

请帮忙理解这个问题。

4个回答

2

Battousai,首先你需要使用刀的反面来达到你的目标,使用可读/可写区域。如果你不这样做,除非编译器/操作系统(Kamiya Kaoru)不阻止你,否则Shishio Makoto可能会通过Sojiro Seta破坏你和你周围重要的人的生活,这些人在你的记忆中,如Sanosuke SagaraYahiko Myojin

strtok将写入您提供的字符串-用null覆盖分隔符,并保留指向剩余字符串的指针。

char *s1 = "1A 2B 3C 4D"; // you have a pointer to some read-only characters
char s2[] = "1A 2B 3C 4D"; // same, decay into pointer
char s3[20] = "1A 2B 3C 4D"; // a twenty element array of characters that you can do what you like with.

1

strtok函数会修改其输入的数据,而字符字面值是只读的。这会导致问题,比如在本例中可能会导致段错误。

其他形式的函数会复制只读数据,因此它们可以正常工作。


1

s1 指向一个字符串字面值,它是常量。任何试图修改字符串字面值的尝试都会导致段错误。


1
s1是指向字符串字面值的指针。它本身不是一个字符串字面值,对吧? - Hitokiri
@Hitokiri,是的,没错,我澄清了我的回答。 - anastaciu

1
问题在于 strtok() 修改了其参数所指向的字符串 - 它不仅仅是解析它,并且当你将 s1 作为参数传递时,你试图修改一个字符串常量。
在你的情况下:
char s2[] = "1A 2B 3C 4D";
char s3[20] = "1A 2B 3C 4D";

s2s3 都是可修改的 char 数组,它们分别保存一个字符串,而:

char *s1 = "1A 2B 3C 4D";

s1 是指向字符串字面量 "1A 2B 3C 4D" 的指针,该字符串是只读的,不能被修改。

任何试图修改字符串字面量的尝试都会导致未定义的行为,在您的情况下会在内存中引发分段错误。

根据 ISO:IEC 9899:2018 (C18) 第6.4.5/7节 - "字符串字面量":

"是否这些数组不同尚未指定,只要它们的元素具有适当的值。 如果程序尝试修改这样的数组,则行为是未定义的。"


s2可修改吗?s2[15] = 'X' 可能吗? - Soner from The Ottoman Empire
@snr 是的,s2 是可修改的,例如 s2[0] = 'X'; 是可以的。但我并没有说可以超出数组的边界写入。 - RobertS supports Monica Cellio

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