Почему я получаю отказ сегментации при записи в строку, инициализированную с “символом *s”, но не “символом s []”?

В Xcode 9.0b5 вы можете столкнуться с этим, потому что Xcode 9.0b5 имеет ошибку в нем, где, когда вы добавляете исходный код, он не соблюдает целевые настройки. Вы должны войти и установить для каждого файла вручную следующие слова:

275
задан Antti Haapala 14 October 2018 в 23:52
поделиться

11 ответов

Посмотрите FAQ C, Вопрос 1.32

Q: Каково различие между этими инициализациями?
char a[] = "string literal";
char *p = "string literal";
Моя программа отказывает, если я пытаюсь присвоить новое значение p[i].

А : строковый литерал (формальный термин для дважды заключенной в кавычки строки в источнике C) может использоваться двумя немного отличающимися способами:

  1. Как инициализатор для массива символа, как в объявлении char a[], это определяет начальные значения символов в том массиве (и, при необходимости, его размер).
  2. Где-либо еще, это превращается в статический массив без имени символов, и этот массив без имени может быть сохранен в постоянной памяти, и который поэтому не может обязательно быть изменен. В контексте выражения массив преобразовывается сразу в указатель, как обычно (см. раздел 6), таким образом, второе объявление инициализирует p для указания на первый элемент массива без имени.

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

227
ответ дан herohuyongtao 23 November 2019 в 02:11
поделиться

Во-первых, str указатель, который указывает на "string". Компилятору позволяют поместить строковые литералы в места в памяти, которую Вы не можете записать в, но можете только считать. (Это действительно должно было инициировать предупреждение, так как Вы присваиваетесь const char * к char *. Вам отключали предупреждения, или Вы просто игнорировали их?)

Во-вторых, Вы создаете массив, который является памятью, что у Вас есть полный доступ к, и инициализация ее с "string". Вы создаете char[7] (шесть для букв, один для завершения '\0'), и Вы делаете то, что Вы любите с ним.

1
ответ дан Bence Kaulics 23 November 2019 в 02:11
поделиться

Строковые литералы как "строка", вероятно, выделяются в адресном пространстве Вашего исполняемого файла как данные только для чтения (плюс-минус Ваш компилятор). Когда Вы идете для касания его, это волнует это, Вы находитесь в его области купального костюма, и сообщает с отказом seg.

В Вашем первом примере, Вы получаете указатель на те данные константы. В Вашем втором примере Вы инициализируете массив 7 символов с копией данных константы.

3
ответ дан Jurney 23 November 2019 в 02:11
поделиться

 char *str = "string";

строка определяет указатель и указывает на него на литеральную строку. Литеральная строка не перезаписываема поэтому, когда Вы делаете:

  str[0] = 'z';

Вы получаете отказ seg. На некоторых платформах литерал мог бы быть в перезаписываемой памяти, таким образом, Вы не будете видеть segfault, но это - недопустимый код (приводящий к неопределенному поведению) независимо.

строка:

char str[] = "string";

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

4
ответ дан Michael Burr 23 November 2019 в 02:11
поделиться

FAQ C, что @matli связал с упоминаниями его, но никем больше здесь, имеет все же, таким образом, для разъяснения: если строковый литерал (дважды заключенная в кавычки строка в Вашем источнике) используется где-нибудь кроме для инициализации символьного массива (т.е.: второй пример @Mark, который работает правильно), та строка хранится компилятором в специальном предложении статическая таблица строк , который сродни созданию глобальной статической переменной (только для чтения, конечно), который является чрезвычайно анонимным (не имеет никакого переменного "имени"). только для чтения часть является важной частью и почему первый пример кода @Mark segfaults.

6
ответ дан rpj 23 November 2019 в 02:11
поделиться
char *str = "string";  

вышеупомянутые наборы str для указания на литеральное значение "string", которое трудно кодируется в двухуровневом изображении программы, которое, вероятно, отмечается как только для чтения в памяти.

Так str[0]= пытается записать в код только для чтения приложения. Я предположил бы, что это - вероятно, зависимый компилятора все же.

7
ответ дан Bence Kaulics 23 November 2019 в 02:11
поделиться

Поскольку тип "whatever" в контексте 1-го примера const char * (даже при присвоении его символу неконстанты*) что означает, что Вы не должны пытаться записать в него.

компилятор осуществил, это путем помещения строки в часть только для чтения памяти, следовательно записи в него генерирует segfault.

12
ответ дан 23 November 2019 в 02:11
поделиться

В первом коде "строка" является строковой константой, и строковые константы никогда не должны изменяться, потому что они часто размещаются в постоянную память. "str" является указателем, используемым для изменения константы.

Во втором коде, "строка" является инициализатором массива, видом стенографии для

char str[7] =  { 's', 't', 'r', 'i', 'n', 'g', '\0' };

, "str" является массивом, выделенным на стеке, и может быть изменен свободно.

17
ответ дан Andru Luvisi 23 November 2019 в 02:11
поделиться

Большинство этих ответов корректно, но только добавить немного больше ясности...

"постоянная память", к которой обращаются люди, является сегментом текста в терминах ASM. Это - то же место в памяти, где инструкции загружаются. Это только для чтения по очевидным причинам как безопасность. При создании символа* инициализированный к строке строковые данные компилируются в сегмент текста, и программа инициализирует указатель для указания в сегмент текста. Таким образом, при попытке изменить его, kaboom. Segfault.

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

33
ответ дан Bob Somers 23 November 2019 в 02:11
поделиться

Обычно, строковые литералы хранятся в постоянной памяти, когда программа запущена. Это должно предотвратить Вас от случайного изменения строковой константы. В Вашем первом примере, "string" хранится в постоянной памяти и *str точки к первому символу. segfault происходит, когда Вы пытаетесь изменить первый символ на 'z'.

Во втором примере, строка "string" , скопировал компилятором от его дома только для чтения до эти str[] массив. Тогда изменение первого символа разрешено. Можно проверить это путем печати адреса каждого:

printf("%p", str);

кроме того, печатая размер str во втором примере покажет Вам, что компилятор выделил 7 байтов для него:

printf("%d", sizeof(str));
98
ответ дан herohuyongtao 23 November 2019 в 02:11
поделиться
char *str = "string";

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

char str[] = "string";

выделяет и инициализирует локальный массив, который является модифицируемый

6
ответ дан Rob Walker 23 November 2019 в 02:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: