В Xcode 9.0b5 вы можете столкнуться с этим, потому что Xcode 9.0b5 имеет ошибку в нем, где, когда вы добавляете исходный код, он не соблюдает целевые настройки. Вы должны войти и установить для каждого файла вручную следующие слова:
Посмотрите FAQ C, Вопрос 1.32
Q: Каково различие между этими инициализациями?
char a[] = "string literal";
char *p = "string literal";
Моя программа отказывает, если я пытаюсь присвоить новое значениеp[i]
.А : строковый литерал (формальный термин для дважды заключенной в кавычки строки в источнике C) может использоваться двумя немного отличающимися способами:
- Как инициализатор для массива символа, как в объявлении
char a[]
, это определяет начальные значения символов в том массиве (и, при необходимости, его размер).- Где-либо еще, это превращается в статический массив без имени символов, и этот массив без имени может быть сохранен в постоянной памяти, и который поэтому не может обязательно быть изменен. В контексте выражения массив преобразовывается сразу в указатель, как обычно (см. раздел 6), таким образом, второе объявление инициализирует p для указания на первый элемент массива без имени.
Некоторые компиляторы имеют управление переключателя, перезаписываемы ли строковые литералы или не (для компиляции старого кода), и у некоторых могут быть опции заставить строковые литералы официально рассматриваться как массивы символа константы (для лучшего, с обнаружением ошибки).
Во-первых, str
указатель, который указывает на "string"
. Компилятору позволяют поместить строковые литералы в места в памяти, которую Вы не можете записать в, но можете только считать. (Это действительно должно было инициировать предупреждение, так как Вы присваиваетесь const char *
к char *
. Вам отключали предупреждения, или Вы просто игнорировали их?)
Во-вторых, Вы создаете массив, который является памятью, что у Вас есть полный доступ к, и инициализация ее с "string"
. Вы создаете char[7]
(шесть для букв, один для завершения '\0'), и Вы делаете то, что Вы любите с ним.
Строковые литералы как "строка", вероятно, выделяются в адресном пространстве Вашего исполняемого файла как данные только для чтения (плюс-минус Ваш компилятор). Когда Вы идете для касания его, это волнует это, Вы находитесь в его области купального костюма, и сообщает с отказом seg.
В Вашем первом примере, Вы получаете указатель на те данные константы. В Вашем втором примере Вы инициализируете массив 7 символов с копией данных константы.
char *str = "string";
строка определяет указатель и указывает на него на литеральную строку. Литеральная строка не перезаписываема поэтому, когда Вы делаете:
str[0] = 'z';
Вы получаете отказ seg. На некоторых платформах литерал мог бы быть в перезаписываемой памяти, таким образом, Вы не будете видеть segfault, но это - недопустимый код (приводящий к неопределенному поведению) независимо.
строка:
char str[] = "string";
выделяет массив символов и копии литеральная строка в тот массив, который полностью перезаписываем, таким образом, последующее обновление не является никакой проблемой.
FAQ C, что @matli связал с упоминаниями его, но никем больше здесь, имеет все же, таким образом, для разъяснения: если строковый литерал (дважды заключенная в кавычки строка в Вашем источнике) используется где-нибудь кроме для инициализации символьного массива (т.е.: второй пример @Mark, который работает правильно), та строка хранится компилятором в специальном предложении статическая таблица строк , который сродни созданию глобальной статической переменной (только для чтения, конечно), который является чрезвычайно анонимным (не имеет никакого переменного "имени"). только для чтения часть является важной частью и почему первый пример кода @Mark segfaults.
char *str = "string";
вышеупомянутые наборы str
для указания на литеральное значение "string"
, которое трудно кодируется в двухуровневом изображении программы, которое, вероятно, отмечается как только для чтения в памяти.
Так str[0]=
пытается записать в код только для чтения приложения. Я предположил бы, что это - вероятно, зависимый компилятора все же.
Поскольку тип "whatever"
в контексте 1-го примера const char *
(даже при присвоении его символу неконстанты*) что означает, что Вы не должны пытаться записать в него.
компилятор осуществил, это путем помещения строки в часть только для чтения памяти, следовательно записи в него генерирует segfault.
В первом коде "строка" является строковой константой, и строковые константы никогда не должны изменяться, потому что они часто размещаются в постоянную память. "str" является указателем, используемым для изменения константы.
Во втором коде, "строка" является инициализатором массива, видом стенографии для
char str[7] = { 's', 't', 'r', 'i', 'n', 'g', '\0' };
, "str" является массивом, выделенным на стеке, и может быть изменен свободно.
Большинство этих ответов корректно, но только добавить немного больше ясности...
"постоянная память", к которой обращаются люди, является сегментом текста в терминах ASM. Это - то же место в памяти, где инструкции загружаются. Это только для чтения по очевидным причинам как безопасность. При создании символа* инициализированный к строке строковые данные компилируются в сегмент текста, и программа инициализирует указатель для указания в сегмент текста. Таким образом, при попытке изменить его, kaboom. Segfault.
, Когда записано как массив, компилятор помещает инициализированные строковые данные в сегмент данных вместо этого, который является тем же местом что Ваши глобальные переменные и такое живое. Эта память изменяема, так как нет никаких инструкций в сегменте данных. На этот раз, когда компилятор инициализирует символьный массив (который является все еще просто символом*), это указывает в сегмент данных, а не сегмент текста, который можно безопасно изменить во времени выполнения.
Обычно, строковые литералы хранятся в постоянной памяти, когда программа запущена. Это должно предотвратить Вас от случайного изменения строковой константы. В Вашем первом примере, "string"
хранится в постоянной памяти и *str
точки к первому символу. segfault происходит, когда Вы пытаетесь изменить первый символ на 'z'
.
Во втором примере, строка "string"
, скопировал компилятором от его дома только для чтения до эти str[]
массив. Тогда изменение первого символа разрешено. Можно проверить это путем печати адреса каждого:
printf("%p", str);
кроме того, печатая размер str
во втором примере покажет Вам, что компилятор выделил 7 байтов для него:
printf("%d", sizeof(str));
char *str = "string";
выделяет указатель на строковый литерал, который компилятор включает немодифицируемую часть Вашего исполняемого файла;
char str[] = "string";
выделяет и инициализирует локальный массив, который является модифицируемый