strtok-char array versus char pointer [дубликат]
Возможный дубликат:
strtok не примет: char * str
При использовании функции strtok
Использование char *
вместо char []
приводит к ошибке сегментации.
Это работает правильно:
char string[] = "hello world";
char *result = strtok(string, " ");
Это приводит к ошибке сегментации:
char *string = "hello world";
char *result = strtok(string, " ");
Может ли кто-нибудь объяснить, чем вызвана эта разница в поведении?6 ответов:
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;
Причина С это позволяет без приведение в основном для того, чтобы позволить древнему коду продолжать компиляцию. Вы должны сделать вид, что тип строкового литерала в исходном коде -
const char[]
, который может преобразовываться вconst char*
, но никогда не преобразуется вchar*
.
Во втором примере:
char *string = "hello world"; char *result = strtok(string, " ");
Указатель
string
указывает на строковый литерал, который не может быть изменен (как хотелось быstrtok()
).Вы могли бы сделать что-то вроде:
char *string = strdup("hello world"); char *result = strtok(string, " ");
Таким образом, что
string
указывает на модифицируемую копию литерала.
strtok
изменяет строку, которую вы передаете ему (или пытается сделать это в любом случае). В первом коде вы передаете адрес массива, который был инициализирован, определенному значению , но поскольку это обычный массив символов, его изменение разрешено.Во втором коде вы передаете адрес строкового литерала. Попытка изменить строковый литерал приводит к неопределенному поведению.
Во втором случае (
char *
) строка находится в памяти только для чтения. Правильный тип строковых констант -const char *
, и если вы используете этот тип для объявления переменной, компилятор предупредит вас, когда вы попытаетесь ее изменить. По историческим причинам вы можете использовать строковые константы для инициализации переменных типаchar *
, даже если они не могут быть изменены. (Некоторые компиляторы позволяют отключить эту историческую лицензию, например, с помощью GCC-Wwrite-strings
.)
В первом случае создается (не const) массив символов, достаточно большой для хранения строки, и инициализируется его содержимым. Во втором случае создается указатель char и инициализируется для указания на строковый литерал, который, вероятно, хранится в памяти только для чтения.
Поскольку strtok хочет изменить память, на которую указывает передаваемый аргумент, последний случай вызывает неопределенное поведение (вы передаете указатель, который указывает на строковый литерал (const)), поэтому его неудивительно, что он падает
Потому что второй объявляет указатель (который может измениться) на постоянную строку...
Так что в зависимости от вашего компилятора / платформы / ОС / карты памяти... строка" hello world " будет сохранена как константа (в встроенной системе она может храниться в ПЗУ), и попытка изменить ее вызовет эту ошибку.