Использование указателя на символ в качестве аргумента strtok - PullRequest
2 голосов
/ 04 апреля 2020

Я пытаюсь разбить строку, используя функцию strtok. Но программа перестала работать, если я использую указатель на символ в качестве аргумента этой функции.

Если я инициализирую строку как s2 или s3, программа работает хорошо. Но если я использую указатель на символ как s1, программа получает Segmentation fault (core dumped).

char *s1 = "1A 2B 3C 4D";
char s2[] = "1A 2B 3C 4D";
char s3[20] = "1A 2B 3C 4D";

Проблема в том, что другие функции printf и strlen работают без сбоев, но только 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)

strtok со страницы man: char *strtok(char *str, const char *delim);

Пожалуйста, помогите понять эту проблему.

Ответы [ 4 ]

2 голосов
/ 04 апреля 2020

Battousai , во-первых, вам нужно использовать обратную сторону катаны, чтобы достичь своей цели с помощью области для чтения / записи. Если вы этого не сделаете, если только компилятор / ОС ( Kamiya Kaoru ) не помешает вам, Shishio Makoto может испортить парней, важных для вас и окружающих, через Sojiro Seta , живущий в вашей памяти, такой как Sanosuke Sagara , Yahiko Myojin .

strtok записывает в строку, которую вы ей даете - перезаписывает символ разделителя нулем и сохраняет указатель на остальную часть строки.

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 голос
/ 04 апреля 2020

Проблема в том, что strtok() изменяет строку, на которую указывает ее аргумент - (она не только анализирует ее), и, задав s1 в качестве аргумента, вы пытаетесь изменить строковый литерал.


В вашем случае:

char s2[] = "1A 2B 3C 4D";
char s3[20] = "1A 2B 3C 4D";

s2 и s3 - это модифицируемые массивы char, каждый из которых содержит строку, тогда как:

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

s1 указатель на строковый литерал "1A 2B 3C 4D", который доступен только для чтения и не может быть изменен.

Любая попытка изменить строковый литерал приводит к неопределенному поведению, а в вашем случае к ошибке сегментации в памяти.

Из ISO: IEC 9899: 2018 (C18) Раздел 6.4.5 / 7 - «Строковые литералы»:

"Не указано, являются ли эти массивы различаются при условии, что их элементы имеют соответствующие значения. Если программа пытается изменить такой массив, поведение не определено."

1 голос
/ 04 апреля 2020

s1 указывает на строковый литерал, который является константой, любая попытка изменить строковый литерал приведет к segfault.

1 голос
/ 04 апреля 2020

strtok изменяет данные, данные ему, и символьные литералы только для чтения. Это вызовет проблемы, в данном случае это ошибка.

Другие формы приведут к созданию копии данных только для чтения, чтобы они работали правильно.

...