strtok - массив символов по сравнению с указателем на символ


person Derek H    schedule 03.11.2010    source источник


Ответы (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;

Причина, по которой C позволяет это без приведения типов, в основном состоит в том, чтобы позволить древнему коду продолжить компиляцию. Вы должны представить, что тип строкового литерала в вашем исходном коде - const char[], который может преобразовывать в const char*, но никогда не преобразовывать его в char*.

person aschepler    schedule 03.11.2010
comment
Много хороших ответов, но я нашел это ярчайшим примером фундаментальной проблемы. - person Derek H; 03.11.2010
comment
+1 отличное объяснение - person R.. GitHub STOP HELPING ICE; 03.11.2010

Во втором примере:

char *string = "hello world";
char *result = strtok(string, " ");

указатель string указывает на строковый литерал, который нельзя изменить (как хотелось бы сделать strtok()).

Вы можете сделать что-то вроде:

char *string = strdup("hello world");
char *result = strtok(string, " ");

так что string указывает на изменяемую копию литерала.

person Michael Burr    schedule 03.11.2010
comment
Я собираюсь сопротивляться желанию -1, но мне очень не нравится этот ответ. Я думаю, это приводит начинающих программистов к идиоме разбрасывания strdup для устранения ошибок сегментации вместо того, чтобы учиться управлять памятью (и особенно строками). Но я не уверен, что было бы лучше, если бы я не сказал, просто используйте массив или динамически выделяйте память для вашей строки. Кстати, strdup не является стандартным C, но, конечно, его достаточно легко реализовать в системах, в которых его нет. - person R.. GitHub STOP HELPING ICE; 03.11.2010

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

Во втором коде вы передаете адрес строкового литерала. Попытка изменить строковый литерал приводит к неопределенному поведению.

person Jerry Coffin    schedule 03.11.2010

Во втором случае (char *) строка находится в постоянной памяти. правильный тип строковых констант - const char *, и если вы использовали этот тип для объявления переменной, вы получите предупреждение от компилятора, когда попытаетесь изменить его. По историческим причинам вам разрешено использовать строковые константы для инициализации переменных типа char *, даже если они не могут быть изменены. (Некоторые компиляторы позволяют отключить эту историческую лицензию, например, с помощью gcc -Wwrite-strings.)

person zwol    schedule 03.11.2010
comment
Также стоит упомянуть, что в первом случае существует неявная копия строкового литерала в массив char. Вот почему у вас нет такой же проблемы. - person Brian Clements; 03.11.2010
comment
const char * на самом деле также приводит к segfault при попытке использования с strtok, но, по крайней мере, выдает предупреждение компиляции. Но отметил, что проблема заключается в модификации. - person Derek H; 03.11.2010
comment
Да, мне следовало быть менее телеграфным. Ответ отредактирован. - person zwol; 03.11.2010
comment
Использование const char * должно приводить к ошибке компилятора, а не предупреждению, если вы передадите его strtok. В языке C нет неявных преобразований, удаляющих квалификаторы; вам нужно явное приведение. - person R.. GitHub STOP HELPING ICE; 04.11.2010
comment
[construct] отбрасывает квалификаторы из целевого типа указателя на самом деле - это просто предупреждение с gcc 4.2, даже с -pedantic. Для char * это тоже не особый случай. - person zwol; 04.11.2010

В первом случае создается массив символов (не константный), достаточно большой для хранения строки, и инициализируется его содержимым. Во втором случае создается указатель на символ и инициализируется так, чтобы он указывал на строковый литерал, который, вероятно, хранится в памяти только для чтения.

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

person Chris Dodd    schedule 03.11.2010

Поскольку второй объявляет указатель (который может изменяться) на постоянную строку ...

Итак, в зависимости от вашего компилятора / платформы / ОС / карты памяти ... строка "hello world" будет сохранена как константа (во встроенной системе она может храниться в ПЗУ), и попытка ее изменить вызовет эту ошибку.

person Matthieu    schedule 03.11.2010