Компиляторы
C не обязаны обнаруживать первую ошибку, поскольку строковые литералы C не являются const
.
Ссылаясь на черновик N1256 стандарта C99 :
6.4.5, абзац 5:
На этапе 7 перевода байт или код нулевого значения добавляются к каждой многобайтовой последовательности символов, которая является результатом строкового литерала или литералов.Последовательность многобайтовых символов затем используется для инициализации массива статической длительности хранения и длины, достаточной только для того, чтобы содержать последовательность.Для строковых литералов символов элементы массива имеют тип char и инициализируются отдельными байтами многобайтовой последовательности символов;[...]
Пункт 6:
Не определено, различаются ли эти массивы при условии, что их элементы имеют соответствующие значения.Если программа пытается изменить такой массив, поведение не определено.
(C11 не меняет этого.)
Таким образом, строковый литерал "hello, world"
имеет тип char[13]
( not const char[13]
), который в большинстве случаев преобразуется в char*
.
Попытка изменить объект const
имеет неопределенное поведение, и большинство кода, который пытается это сделатьтак должен быть диагностирован компилятором (вы можете обойти это, например, с помощью приведения).Попытка изменить строковый литерал также ведет к неопределенному поведению, но не потому, что это const
(это не так);это потому, что стандарт специально говорит, что поведение не определено.
Например, эта программа строго соответствует:
#include <stdio.h>
void print_string(char *s) {
printf("%s\n", s);
}
int main(void) {
print_string("Hello, world");
return 0;
}
Если строковые литералы были const
, то передача "Hello, world"
вфункция, которая принимает (не const
) char*
, потребует диагностики.Программа действительна, но она будет демонстрировать неопределенное поведение, если print_string()
попытается изменить строку, на которую указывает s
.
Причина является исторической.В пре-ANSI C не было ключевого слова const
, поэтому не было способа определить функцию, которая принимает char*
и обещает не изменять то, на что оно указывает.Создание строковых литералов const
в ANSI C (1989) нарушило бы существующий код, и не было хорошей возможности сделать такое изменение в более поздних выпусках стандарта.
gcc's -Wwrite-strings
делаетзаставляет его обрабатывать строковые литералы как const
, но делает gcc несовместимым компилятором, так как он не выдает диагностическое сообщение:
const char (*p)[6] = &"hello";
("hello"
имеет тип char[6]
,поэтому &"hello"
имеет тип char (*)[6]
, что несовместимо с объявленным типом p
. С -Wwrite-strings
, &"hello"
рассматривается как тип const char (*)[6]
.) Предположительно, поэтому ни -Wall
кроме того, -Wextra
включает -Wwrite-strings
.
С другой стороны, код, который вызывает предупреждение с -Wwrite-strings
, вероятно, следует исправить в любом случае.Это неплохая идея написать свой код на C, чтобы он компилировался без диагностики как с -Wwrite-strings
.
, так и без него (обратите внимание, что строковые литералы C ++ равны const
, потому что когда Бьярн Страуструпразрабатывал C ++, он не был так обеспокоен строгой совместимостью со старым кодом C.)