Почему строковые литералы const?
известно, что в C++ строковые литералы неизменяемы и результат изменения строкового литерала не определен. Например
char * str = "Hello!";
str[1] = 'a';
это приведет к неопределенному поведению.
кроме того, строковые литералы помещаются в статическую память. Так они существуют на протяжении всей программы. Я хотел бы знать, почему строковые литералы имеют такие свойства.
5 ответов
есть несколько различных причин.
один из них-разрешить хранение строковых литералов в памяти только для чтения (как уже упоминалось).
другой-разрешить слияние строковых литералов. Если одна программа использует один и тот же строковый литерал в нескольких разных местах, приятно разрешить (но не обязательно требовать) компилятору объединить их, поэтому вы получаете несколько указателей на одну и ту же память, а не каждый занимает отдельный кусок памяти. Это также может применить, когда два строковых литерала не обязательно одинаковых, но имеют такое же окончание:
char *foo = "long string";
char *bar = "string";
В таком случае, это возможно для bar
на foo+5
(Если я правильно посчитал).
в любом из этих случаев, если вы разрешите изменить строковый литерал, он может изменить другое строковый литерал, который имеет одинаковое содержимое. В то же время, честно говоря, не так много смысла в мандате, что либо-это довольно необычно иметь достаточно строковых литералов, которые вы могли бы перекрывать, что большинство людей, вероятно, хотят, чтобы компилятор работал медленнее, чтобы сохранить (возможно) несколько десятков байтов памяти.
к моменту написания первого стандарта уже были компиляторы, которые использовали все три из этих методов (и, вероятно, несколько других). Поскольку невозможно описать одно поведение, которое вы получите от изменения строкового литерала, и никто, по-видимому, не думал, что это важная возможность поддержка, они сделали очевидное: сказали, что даже попытка сделать это привела к неопределенному поведению.
это неопределенное поведение для изменения литерала, потому что стандарт так говорит. И стандарт говорит так, чтобы позволить компиляторам помещать литералы в память только для чтения. И делает это по ряду причин. Один из которых-позволить компиляторам оптимизировать хранение только одного экземпляра литерала, который повторяется много раз в источнике.
Я считаю, что вы спрашиваете о причине, по которой литералы помещаются в только для чтения, а не о технических деталях компоновщика, делающего это и это или юридические детали стандарта, запрещающего то-то и то-то.
при модификации строковых литералов работает, это приводит к тонким ошибкам даже при отсутствии слияния строк (на что у нас есть причины запретить, если мы решили разрешить изменения). Когда вы видите код как
char *str="Hello";
.../* some code, but str and str[...] are not modified */
printf("%s world\n", str);
это естественный вывод, что вы знаю что будет напечатано,
потому что str
(и его содержания) не были изменены, в частности
место между инициализацией и использованием.
однако, если строковые литералы доступны для записи, вы не знаю любой подробнее: str[0] может быть перезаписан позже, в этом коде или внутри глубоко вложенный вызов функции и при повторном запуске кода,
char *str="Hello";
ничего не гарантирует о содержании из понять этот код как сброс str
"Привет". Трудно
преодолейте это естественное понимание, и будет трудно рассуждать о
код, где это не гарантируется. Когда вы видите выражение типа
x+14
, а если бы вам пришлось думать о 14 возможно перезаписывается
другими словами, стало 42? Та же проблема со строками.
это причина запретить модификацию строковых литералов, как в стандарт (без требования обнаружить отказ раньше) и внутри фактические целевые платформы (предоставление бонуса обнаружения потенциала жуки.)
Я считаю, что многие попытки объяснить им, что такое страдать от худший вид круговых рассуждений. Стандарт запрещает писать константы потому что компилятор может объединить строки, или они могут быть размещены в памяти только для чтения. Они помещены в read-only память для того чтобы уловить нарушение стандарта. И это действительно для слияния литералов, потому что стандарт запрещает... это своего рода объяснение, о котором вы просили?
давайте посмотрим на другие языки. общий стандарт Lisp делает модификацию литералов неопределенным поведением, даже если история предыдущих шепелявит сильно отличается с историей С реализации. Это потому, что записываемые литералы логически опасный. Языковые стандарты и схемы памяти отражают только то, что факт.
язык Python имеет ровно одно место, где что-то похожее "запись в литералы" может произойти: значения по умолчанию параметра, и это факт путает людей все время.
Ваш вопрос с тегами C++
и я не уверен в его нынешнем состоянии
в отношении неявное преобразование не-const char*
: если это
обращение, оно устарело? Я ожидаю, что другие ответы предоставят
полное просветление по этому вопросу. Как мы говорим о другое языки
здесь, позвольте мне упомянуть простой C. Здесь строковые литералы не const,
и эквивалентным вопросом было бы почему я не могу изменить строку
литералы (и люди с большим опытом спрашивают вместо этого,почему
строковые литералы non-const, если я не могу изменить их!--14-->?). Однако
рассуждение выше полностью применимо к C, несмотря на это различие.
потому что это K&R C, не было такой вещи, как"const". И аналогично в pre-ANSI C++. Следовательно, было много кода, который имел такие вещи, как char * str = "Hello!";
Если бы Комитет по стандартам сделал текстовые литералы const, все эти программы больше не компилировались бы. Поэтому они пошли на компромисс. Текстовые литералы являются официальными const char[]
, но у них есть молчаливое неявное преобразование в char*
.
в 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
исправлена строковые литералы в Си)