И где литералы в памяти точно? (см. примеры ниже),
Я не могу изменить литерал, таким образом, это, предположительно, был бы символ константы*, хотя компилятор позволил мне использовать символ* для него, у меня нет предупреждений даже с большинством флагов компилятора.
Принимая во внимание, что неявный бросок символа константы* тип к символу* тип дает мне предупреждение, посмотрите ниже (протестированный на 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 */
Стандарт C не запрещает модификацию строковых литералов. Он просто говорит, что поведение не определено, если такая попытка предпринята. Согласно обоснованию C99, в комитете были люди, которые хотели, чтобы строковые литералы были модифицируемыми, поэтому стандарт не запрещает это в явном виде.
Обратите внимание, что в C++ ситуация иная. В C++ строковые литералы являются массивами const char. Однако C++ допускает преобразования из const char * в char *. Однако эта возможность уже устарела.
А где именно литералы в памяти? (см. примеры ниже)
Инициализированный сегмент данных. В Linux это либо .data
, либо .rodata
.
Я не могу изменить литерал, поэтому предположительно это будет const char *, хотя компилятор разрешил мне использовать для него char *, у меня нет предупреждений даже с большинством флагов компилятора.
Исторический, как это уже объяснялось другими. Большинство компиляторов позволяют указать, должны ли строковые литералы быть доступными только для чтения или изменяться с помощью параметра командной строки.
Причина, по которой обычно желательно иметь строковые литералы только для чтения, заключается в том, что сегмент с данными только для чтения в памяти может (и обычно используется) совместно использоваться всеми процессами, запускаемыми из исполняемого файла. Это, очевидно, освобождает часть оперативной памяти от потери на хранение избыточных копий одной и той же информации.
Я не уверен в том, что стандарты 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». Он находится в памяти для чтения / записи и может быть изменен. Здесь нет проблем.
У меня нет предупреждений даже с большинством флагов компилятора
Правда? Когда я компилирую следующий фрагмент кода:
int main()
{
char* p = "some literal";
}
на g++ 4.5.0 даже без каких-либо флагов, я получаю следующее предупреждение:
warning: deprecated conversion from string constant to 'char*'
Вы можете писать в c
, потому что вы не сделали его const. Определение c
как const было бы правильной практикой, так как правая часть имеет тип const char*
.
Это генерирует ошибку во время выполнения, поскольку значение "test", вероятно, выделено в сегменте кода, который доступен только для чтения. См. здесь и здесь.
В основном по историческим причинам. Но имейте в виду, что они в некоторой степени оправданы: строковые литералы не имеют типа char *
, но char [N]
, где N
обозначает размер buffer (в противном случае sizeof
не будет работать должным образом со строковыми литералами) и может использоваться для инициализации массивов, отличных от const
. Их можно присвоить только указателям const
из-за неявного преобразования массивов в указатели и не- const
в const
.
Было бы более последовательным, если бы строковые литералы демонстрировали то же поведение, что и составные литералы, но поскольку они являются конструкцией C99 и необходимо поддерживать обратную совместимость, это не вариант, поэтому строковые литералы остаются исключительным случаем.