Почему компиляторы позволяют строковым литералам не быть константой?

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

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

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

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

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 */
9
задан hrnt 19 June 2010 в 10:59
поделиться

6 ответов

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

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

12
ответ дан 4 December 2019 в 12:59
поделиться

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

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

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

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

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

1
ответ дан 4 December 2019 в 12:59
поделиться

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

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

Но вам следует знать кое-что еще. Не все выражения 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 не делает ' t означает что-либо на машинном уровне

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

Во-первых, приведенное выше не относится к строковым литералам. 'a' не является строкой. Это символ. Это просто число.Это как написать следующее:

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

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

Я хотел бы добавить еще один случай:

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

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

1
ответ дан 4 December 2019 в 12:59
поделиться

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

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

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

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

warning: deprecated conversion from string constant to 'char*'

0
ответ дан 4 December 2019 в 12:59
поделиться

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

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

0
ответ дан 4 December 2019 в 12:59
поделиться

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

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

2
ответ дан 4 December 2019 в 12:59
поделиться