Почему ошибка сегментации для изменения неконстантного символа *? - PullRequest
2 голосов
/ 05 августа 2011

С этим кодом я получаю ошибку сегментации:

   char* inputStr = "abcde";
   *(inputStr+1)='f';

Если код был:

   const char* inputStr = "abcde";
   *(inputStr+1)='f';

Я получу ошибку компиляции для "назначения местоположения только для чтения".Однако в первом случае ошибки компиляции нет;только ошибка сегментации, когда операция присвоения действительно произошла.

Кто-нибудь может объяснить это?

Ответы [ 9 ]

6 голосов
/ 05 августа 2011

Вот что стандарт говорит о строковых литералах в разделе [2.13.4 / 2]:

Строковый литерал, который не начинается с u, U или L, является обычным строковым литераломтакже называется узким строковым литералом.Обычный строковый литерал имеет тип «массив из n const char», где n - размер строки, как определено ниже;он имеет статическую продолжительность хранения (3.7) и инициализируется заданными символами.

Итак, строго говоря, «abcde» имеет тип

const char[6]

Теперь, что происходит в вашем кодеявляется неявным приведением к

char*

, так что назначение разрешено.Причина этого, вероятно, заключается в совместимости с C. Посмотрите также на обсуждение здесь: http://learningcppisfun.blogspot.com/2009/07/string-literals-in-c.html

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

3 голосов
/ 05 августа 2011

Это создается в сегменте кода:

char *a = "abcde";

По сути, это постоянное значение.

Если вы хотите изменить его, попробуйте:

char a[] = "abcde";
2 голосов
/ 05 августа 2011

Стандарт гласит, что вам не разрешено изменять строковые литералы напрямую, независимо от того, помечаете ли вы их const или нет:

Все ли строковые литералы различны (то есть сохраняютсяв неперекрывающихся объектах) определяется реализацией.Эффект попытки изменить строковый литерал не определен.

На самом деле, в C (в отличие от C ++) строковые литералы имеют вид , а не const, но вы все еще неразрешено писать им.

Это ограничение на запись позволяет выполнять определенные оптимизации, такие как совместное использование литералов в соответствии с:

char *ermsg = "invalid option";
char *okmsg =   "valid option";

, где okmsg может фактически указывать насимвол 'v' в ermsg, а не отдельная строка.

1 голос
/ 05 августа 2011

Произошло то, что компилятор поместил константу "abcde" в некоторый сегмент памяти только для чтения.Вы указали (неконстантно) char* inputStr на эту константу, а kaboom, segfault.

Урок, который необходимо выучить: не вызывать неопределенное поведение.

Редактировать (подробно))

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

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

1 голос
/ 05 августа 2011

Это в основном древняя история; давным-давно строковые литералы не были постоянными.

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

С G ++ вы наверняка можете получить предупреждение о компиляции (-Wall, если оно не включено по умолчанию). Например, G ++ 4.6.0, скомпилированный на MacOS X 10.6.7 (но работающий на 10.7), дает:

$ cat xx.cpp
int main()
{
    char* inputStr = "abcde";
   *(inputStr+1)='f';
}
$ g++ -c xx.cpp
xx.cpp: In function ‘int main()’:
xx.cpp:3:22: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
$

Таким образом, предупреждение включено по умолчанию.

1 голос
/ 05 августа 2011

Строковые литералы обычно хранятся в постоянной памяти.Попытка изменить эту память убьет вашу программу.

Вот хорошее объяснение: Строковый литерал в c ++ создан в статической памяти?

0 голосов
/ 02 сентября 2014

немного истории строковых литералов в словах Ричи. в основном о происхождении и эволюции строковых литералов из K & R 1. Надеюсь, это прояснит одну или две вещи о константных и строковых литералах.

"От: Деннис Ритчи Тема: Re: История вопроса: Строковые литералы. Дата: 02 июня 1998 Группы новостей: comp.std.c

В то время, когда комитет C89 работал, доступный для записи строковые литералы не были "устаревшим кодом" (Марголин) и какой стандарт существовало (K & R 1) было довольно явно (A.2.5), что Строки были просто способом инициализации статического массива. И, как отметил Барри, были некоторые (mktemp) процедуры который использовал этот факт.

Я не был на совещании комитета по точка, но я подозреваю, что утилита BSD для возни код ассемблера для перемещения инициализации строк текст вместо данных, и осознание того, что большинство буквальные строки на самом деле не были перезаписаны, было больше важнее, чем какая-то очень ранняя версия gcc.

Где, я думаю, комитет мог что-то упустить не в состоянии найти формулировку, которая объяснила поведение строковых литералов в терминах конст. То есть, если «abc» является анонимным литералом типа константный символ [4] тогда почти все его свойства (включая возможность делать только для чтения и даже делиться своим хранилищем с другими вхождениями того же буквального) почти объяснил.

Проблема с этим была не только относительно места, на которых действительно были написаны строковые литералы, но более важно, разработка возможных правил для назначений указатели на константу, в частности для фактической функции аргументы. Реально комитет знал, что все сформулированные ими правила не могли требовать обязательного диагностика для каждого функционала («строки») в существующем мире.

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

Кстати, эта заметка не предназначена для чтения как бекас по формулировке в С89. Это очень трудно получить вещи как правильный (связный и правильный) и пригодный для использования (последовательный достаточно, достаточно привлекательно).

Dennis

"

0 голосов
/ 05 августа 2011

Строковые литералы, хотя они официально неконстантны, почти всегда хранятся в постоянной памяти. В вашей настройке это, по-видимому, только в том случае, если он объявлен как массив const char.

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

0 голосов
/ 05 августа 2011

Несмотря на то, что "abcde" является строковым литералом, который не должен изменяться, вы сказали компилятору, что вас это не волнует, если на него указывает неконстантный char*.

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

...