Почему строковые литералы const?

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

char * str = "Hello!";
str[1] = 'a';

Это приведет к неопределенному поведению.

Кроме того, строковые литералы помещаются в статическую память. Таким образом, они существуют в течение всей программы. Я хотел бы знать, почему строковые литералы имеют такие свойства.

12

Решение

Есть несколько разных причин.

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

Другое — разрешить слияние строковых литералов. Если одна программа использует один и тот же строковый литерал в нескольких разных местах, было бы неплохо разрешить (но не обязательно) компилятору объединить их, чтобы вы получили несколько указателей на одну и ту же память вместо того, чтобы каждый занимал отдельный кусок памяти. Это также может применяться, когда два строковых литерала не обязательно идентичный, но имеют одинаковое окончание:

char *foo = "long string";
char *bar = "string";

В таком случае это возможно для bar быть foo+5 (если бы я считал правильно).

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

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

18

Другие решения

Модификация литерала — неопределенное поведение, потому что стандарт так говорит. И в стандарте говорится, что компиляторы могут помещать литералы в постоянную память. И делает это по ряду причин. Один из них — позволить компиляторам оптимизировать хранение только одного экземпляра литерала, который многократно повторяется в источнике.

12

Я полагаю, вы спросите о причине, по которой литералы
только для чтения, а не о технических деталях компоновщика, делающего это и
то или юридические детали стандарта, запрещающего то-то и то-то.

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

char *str="Hello";
.../* some code, but str and str[...] are not modified */
printf("%s world\n", str);

это естественный вывод, что вы знать что будет напечатано,
так как str (и его содержание) не были изменены в конкретном
место, между инициализацией и использованием.

Однако, если строковые литералы доступны для записи, вы не знаю это любой
больше: str [0] может быть перезаписано позже, в этом коде или внутри
глубоко вложенный вызов функции, и когда код запускается снова,

char *str="Hello";

не будет ничего гарантировать о содержании str больше. Как мы
ожидаемо, эта инициализация реализована как перемещение известного адреса
во время ссылки в место для str, Это не проверяет, что str
содержит «Hello» и не выделяет новую его копию. Тем не мение,
мы Понимаю этот код как сброс str на «Привет». Это трудно, чтобы
преодолеть это естественное понимание, и трудно рассуждать о
код, где это не гарантируется. Когда вы видите выражение, как
x+14что если бы вам пришлось подумать о том, что 14 может быть перезаписано
в другом коде, так стало 42? Та же проблема со строками.

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

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

Давайте посмотрим на другие
языки. Стандарт Common Lisp
делает модификацию литералов неопределенным поведением, даже если
История предыдущих Лиспов сильно отличается от истории С
Реализации. Это потому, что записываемые литералы логически
опасно. Языковые стандарты и макеты памяти только отражают это
факт.

Язык Python имеет ровно одно место, где что-то напоминающее
может произойти «запись в литералы»: значения параметров по умолчанию, и это
факт смущает людей все время.

Ваш вопрос помечен C++и я не уверен в его текущем состоянии
в отношении неявное преобразование неконстантным char*: если это
преобразование, это устарело? Я ожидаю, что другие ответы, чтобы обеспечить
полное просветление по этому вопросу. Как мы говорим о Другой языки
здесь, позвольте мне упомянуть простой C. Здесь строковые литералы не Const,
и эквивалентный вопрос будет почему я не могу изменить строку
литералы
(и люди с большим опытом спрашивают, Почему
строковые литералы неконстантны, если я не могу их изменить
?). Тем не менее
рассуждения выше полностью применимы к С, несмотря на эту разницу.

2

Потому что это К&R C, не было такой вещи, как «const». И аналогично в pre-ANSI C ++. Следовательно, было много кода, который имел такие вещи, как char * str = "Hello!"; Если бы Комитет по стандартам сделал текстовые литералы постоянными, все эти программы больше не компилировались бы. Поэтому они пошли на компромисс. Текстовые литералы являются официальными const char[], но у них есть молчаливое неявное преобразование в char*,

1

В C ++ строковые литералы const потому что вам не разрешено
изменить их. В стандарте С они были бы const как
ну разве что когда const был введен в C, там был
так много кода по линии char* p = "somethin"; тот
сделав их const сломался бы, это считалось
неприемлемый. (Комитет C ++ выбрал другое решение для
эта проблема, с устаревшим неявным преобразованием, которое позволяет
выше.)

В оригинальном C строковые литералы были не const, и были
изменяемый, и было гарантировано, что нет двух строковых литералов
любая память. Это было быстро понято как серьезная ошибка,
позволяя такие вещи, как:

void
mutate(char* p)
{
static char c = 'a';
*p = a ++;
}

и в другом модуле:

mutate( "hello" );  //  Can't trust what is written, can you.

(Некоторые ранние реализации Fortran имели похожую проблему,
где F(4) может позвонить F практически с любым интегральным значением.
Комитет Фортрана это исправил, как и комитет С
исправлены строковые литералы в C.)

0
По вопросам рекламы [email protected]