Подтвердить что ты не робот

Почему компиляторы C не предупреждают о несовместимых типах с литеральными строками?

Следующая программа вызывает системную ошибку сегментации из-за поведения undefined (пытается изменить строковый литерал):

int main() {
  char *s = "immutable";
  s[0] = 'a';
  return 0;
}

Тем не менее, похоже, абсолютно невозможно сказать, что GCC/Clang излучает даже малейшее предупреждение об этом (-Wall -Wextra -pedantic -std=c11 ничего не делать).

Специально для новичков такая ситуация была бы полезной для информирования. Даже для не-новичков, в некоторых немного менее очевидных ситуациях это может быть полезно:

void f(char *s) {
  s[0] = '0';
}

int main() {
  char *s = "immutable";
  f("literal"); // oops
  f(s); // oops
  return 0;
}

Кроме того, это поможет принудительно выполнить некоторые const -культуры в программировании на языке C.

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

4b9b3361

Ответ 1

TL; DR Компиляторы C не предупреждают, потому что они не видят проблему там. По определению, строковые литералы C имеют нулевое завершение char массивов. Он только заявил, что

[...] Если программа пытается изменить такой массив, поведение undefined.

Итак, в процессе компиляции компилятор не знает, что массив char должен вести себя как строковый литерал или строка. Только попытка модификации запрещена.

Связанное чтение: для любого, кого интересует, см. Почему C-строковые литералы доступны только для чтения?

Тем не менее, я не очень уверен, что это хороший вариант, но gcc имеет параметр -Wwrite-strings.

Цитирование онлайн-руководство,

-Wwrite-strings

При компиляции C задайте строковые константы типа const char[length], так что копирование адреса одного в указатель не const char * вызывает предупреждение. Эти предупреждения помогут вам найти код времени компиляции, который может попытаться записать в строчную константу, но только если вы были очень осторожны в использовании const в объявлениях и прототипах. В противном случае это всего лишь неприятность. Вот почему мы не запрашивали эти предупреждения -Wall.

Таким образом, он выдает предупреждение, используя бэкдор-путь.

По определению, строковые литералы C (то есть литералы символьной строки) являются char массивами с нулевым терминатором. Стандарт не позволяет им быть const квалифицированным.

Ссылка: C11, глава

В фазе 7 перевода байт или код нулевого значения добавляется к каждому мультибайту последовательность символов, которая получается из строкового литерала или литералов. Многобайтовый символ последовательность используется для инициализации массива статической продолжительности хранения и длины достаточный для того, чтобы содержать последовательность. Для символьных строковых литералов элементы массива имеют введите char и инициализируются отдельными байтами многобайтового символа последовательность. [....]

Использование вышеуказанной опции делает строковые литералы const квалифицированными так, используя строковый литерал, так как RHS присвоения указателю неконтекстного типа вызывает предупреждение.

Это делается со ссылкой на C11, глава §6.7.3

Если предпринимается попытка изменить объект, определенный с помощью типа const, используя lvalue с неконстанционно-квалификационным типом, поведение undefined. [...]

Итак, здесь компилятор выдает предупреждение для присвоения const квалифицированного типа неквалифицированному типу <<29 > .

Связано с тем, почему использование -Wall -Wextra -pedantic -std=c11 не вызывает этого предупреждения, цитирует цитату еще раз

[...] Эти предупреждения помогут вам найти код времени компиляции, который может попытаться записать в константу строки, но только если вы были очень осторожны в использовании констант в объявлениях и прототипах. В противном случае это всего лишь неприятность. Вот почему мы не запрашивали эти предупреждения -Wall.

Ответ 2

Для этого есть опция: -Wwrite-strings. Он работает, изменяя тип строковых литералов от char[N] до const char[N]. Это изменение несовместимо со стандартом C и приведет к отклонению действительного кода, а в редких случаях недействительный код будет принят молча. По умолчанию он не включен.

К сожалению, из-за того, что строковые литералы определены в C, предоставление хороших предупреждений для этого без изменения языка очень сложно.