Почему компиляторы позволяют строковым литералам не быть константами? - PullRequest
8 голосов
/ 19 июня 2010

А где именно в памяти литералы? (см. примеры ниже)

Я не могу изменить литерал, так что якобы это будет const char *, хотя компилятор позволил мне использовать для него char *, у меня нет предупреждений даже с большинством флагов компилятора.

Принимая во внимание, что неявное приведение типа const char * к типу char * дает мне предупреждение, см. Ниже (протестировано на GCC, но оно ведет себя аналогично на VC ++ 2010).

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

const char* a = "test";
char* b = a; /* warning: initialization discards qualifiers 
  from pointer target type (on gcc), error on VC++2k10 */

char *c = "test"; // no compile errors
c[0] = 'p'; /* bus error when execution (we are not supposed to 
  modify const anyway, so why can I and with no errors? And where is the 
  literal stored for I have a "bus error"? 
  I have 'access violation writing' on VC++2010 */

const char d = 'a';
*(char*)&d = 'b'; // no warnings (why not?)
printf("%c", d);  /* displays 'b' (why doesn't it do the same
  behavior as modifying a literal? It displays 'a' on VC++2010 */

Ответы [ 6 ]

11 голосов
/ 19 июня 2010

Стандарт C не запрещает модификацию строковых литералов. Это просто говорит о том, что поведение не определено, если предпринята попытка. Согласно обоснованию C99, в комитете были люди, которые хотели, чтобы строковые литералы были модифицируемыми, поэтому стандарт прямо не запрещает это.

Обратите внимание, что в C ++ ситуация иная. В C ++ строковые литералы являются массивами const char. Тем не менее, C ++ допускает преобразования из const char * в char *. Однако эта функция устарела.

2 голосов
/ 19 июня 2010

В основном исторические причины. Но имейте в виду, что они несколько оправданы: строковые литералы не имеют типа char *, но char [N], где N обозначает размер буфера (в противном случае sizeof не будет работать должным образом для строковых литералов ) и может использоваться для инициализации не const массивов. Вы можете назначить их только указателям const из-за неявного преобразования массивов в указатели и не const в const.

Было бы более логичным, если бы строковые литералы демонстрировали то же поведение, что и составные литералы, но, поскольку это конструкция C99, и необходимо поддерживать обратную совместимость, это не вариант, поэтому строковые литералы остаются в исключительном случае.

1 голос
/ 19 июня 2010

Я не уверен, что стандарты C / C ++ обозначают строки.Но я могу сказать точно , что на самом деле происходит со строковыми литералами в MSVC.И, я полагаю, другие компиляторы ведут себя аналогично.

Строковые литералы находятся в разделе данных const.Их память отображается в адресном пространстве процесса.Однако страницы памяти, в которых они хранятся, предназначены только для чтения (если явно не изменены во время выполнения).

Но есть кое-что еще, что вы должны знать.Не все выражения C / C ++, содержащие кавычки, имеют одинаковое значение.Давайте все проясним.

const char* a = "test";

Вышеприведенный оператор заставляет компилятор создавать строковый литерал "test".Компоновщик гарантирует, что он будет в исполняемом файле.В теле функции компилятор генерирует код, который объявляет переменную a в стеке, которая инициализируется адресом строкового литерала "test.

char* b = a;

Здесь вы объявляете другую переменную bв стеке, который получает значение a. Поскольку a указывает на адрес только для чтения - то же самое можно сказать и о b. Четный факт b не имеет семантики const, не означает, что вы можете изменить то, чтоон указывает на

char *c = "test"; // no compile errors
c[0] = 'p';

Вышеуказанное приводит к нарушению доступа. Опять же, отсутствие const ничего не значит на уровне машины

const char d = 'a';
*(char*)&d = 'b';

Прежде всего -вышеприведенное не относится к строковым литералам. 'a' - это не строка. Это символ. Это просто число. Это похоже на написание следующего:

const int d = 55;
*(int*)&d = 56;

Приведенный выше код делает дурака из компилятораВы говорите, что переменная const, однако вам удается ее изменить. Но это не связано с исключением процессора, поскольку, тем не менее, d находится в памяти для чтения / записи.

Я бы хотелдобавить один разe case:

char b[] = "test";
b[2] = 'o';

Выше объявляется массив в стеке и инициализируется со строкой "test".Он находится в памяти для чтения / записи и может быть изменен.Здесь нет проблем.

1 голос
/ 19 июня 2010

А где именно в памяти литералы?(см. примеры ниже)

Инициализированный сегмент данных.В Linux это либо .data, либо .rodata.

. Я не могу изменить литерал, так что якобы это будет const char *, хотя компилятор позволил мне использовать для него char *,У меня нет предупреждений даже с большинством флагов компилятора.

Исторический, как это уже объясняли другие.Большинство компиляторов позволяют указывать, должны ли строковые литералы быть доступны только для чтения или модифицироваться с помощью параметра командной строки.

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

0 голосов
/ 19 июня 2010

Вы можете написать на c, потому что вы не сделали его постоянным. Определение c как const было бы правильной практикой, поскольку правая сторона имеет тип const char*.

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

0 голосов
/ 19 июня 2010

У меня нет предупреждений, даже с большинством флагов компилятора

Правда?Когда я компилирую следующий фрагмент кода:

int main()
{
    char* p = "some literal";
}

на g ++ 4.5.0 даже без каких-либо флагов , я получаю следующее предупреждение:

предупреждение:устаревшее преобразование из строковой константы в 'char *'

...