Почему компиляторы C затрудняют получение предупреждений -Wwrite-strings? - PullRequest
2 голосов
/ 25 апреля 2020

Рассмотрим этот C код:

void foo(char *);

void bar(void) {
    foo("");
}

Когда я компилирую это с -pedantic -Wall -Wextra с G CC или Clang, или с -Weverything с Clang, он компилируется, не давая мне никакого отношения предупреждения. Если я добавлю -Wwrite-strings, то G CC даст мне следующее:

<source>:4:9: warning: passing argument 1 of 'foo' discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
    4 |     foo("");
      |         ^~
<source>:1:10: note: expected 'char *' but argument is of type 'const char *'
    1 | void foo(char *);
      |          ^~~~~~

И Clang даст мне следующее:

<source>:4:9: warning: passing 'const char [1]' to parameter of type 'char *' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers]
    foo("");
        ^~
<source>:1:16: note: passing argument to parameter here
void foo(char *);
               ^

Мне многое кажется неправильным:

  • Это довольно важное предупреждение, которое не только отключается по умолчанию, но и отключается даже при большинстве способов включения предупреждений на практике.
  • G CC вывод * относится к -Wdiscarded-qualifiers, но если я передам это вместо -Wwrite-strings, я не получу это предупреждение.
  • Я думал, что -Weverything означало "буквально каждое предупреждение, о котором знает компилятор ", но это, кажется, противоречит этому.
  • Если я скомпилирую тот же код, что и C ++, а не как C, то и G CC, и Clang выдают мне предупреждение, которое мне нужно, без необходимости устанавливать какие-либо флаги компилятора в все. Я не уверен, почему здесь есть разница между языками, так как AFAIK, и C, и C ++ имеют неопределенное поведение, если вы действительно пишете в строковый литерал.

Что здесь происходит? Почему это конкретное предупреждение кажется таким глючным?

Ответы [ 2 ]

5 голосов
/ 25 апреля 2020

В C строковые литералы существовали раньше, чем const. Таким образом, строковые литералы C не были константно квалифицированы (хотя результаты попыток записи в них не определены стандартом C). Если строковые литералы были сделаны константно квалифицированными, многие старые программы сломались бы из-за ошибок типов. Комитет C решил, что это изменение не имеет смысла.

Переключатель -Wwrite-strings на самом деле не является предупреждением, несмотря на -W. Это изменяет компилируемый язык на нестандартный C, в котором строковые литералы являются константными. Это объясняет, почему G CC показывает -Wdiscarded-qualifier, когда строковому литералу присваивается char *, а также объясняет, почему только -Wdiscarded-qualifier не вызывает эти предупреждения - потому что без -Wwrite-strings строковый литерал не является с квалификацией const, поэтому при отборе не отбрасывается ни один классификатор.

Предположительно, Clang -Weverything не включает -Wwrite-strings, поскольку, как отмечалось выше, это не является действительно параметром предупреждения и потому что он меняет язык к нестандартному C.

2 голосов
/ 25 апреля 2020

Это довольно важное предупреждение, которое не только отключено по умолчанию, но и отключено даже при большинстве способов включения предупреждений на практике.

Что ж, авторы g cc не согласны с вами, и они объяснили, почему в руководстве:

-Wwrite-strings

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

Это также объясняет ваш второй вопрос: вывод

G CC относится к -Wdiscarded-qualifiers, но если я передам это вместо - Записать строки, я не получаю это предупреждение.

Потому что без -Wwrite-strings строковый литерал просто имеет тип char[], поэтому ни один классификатор не отбрасывается.

(Это также объясняет, почему в сообщении упоминается -Wdiscarded-qualifiers вместо -Wwrite-strings; проблема на самом деле в отбрасываемом квалификаторе, и код предупреждения для этого не возвращает вас к сообщению, что единственная причина была Во-первых, квалификатор был из-за другого варианта. Это было бы ужасно сложно.)

Я думал, что все означало «буквально каждое предупреждение, о котором знает компилятор», но это, кажется, противоречит что.

Да, это то, о чем говорится в руководстве clang:

В дополнение к традиционным флагам -W, можно включить всю диагностику мимоходом Это работает, как и ожидалось, с -Werror, а также включает предупреждения от -pedanti c.

Действительно, это уже было зарегистрировано как ошибка: https://bugs.llvm.org/show_bug.cgi?id=18801. Очевидно, -Weverything включил это предупреждение в clang 3.4 и более ранних версиях, но произошла регрессия или преднамеренное изменение, которое не было задокументировано. Eri c Ответ Постписила дает правдоподобную причину, по которой он мог быть преднамеренным: он не только включает предупреждение, но и фактически изменяет языковой диалект.

Если я компилирую тот же код, что и C ++, вместо C, тогда и G CC, и Clang выдают мне предупреждение, которое я хочу, без необходимости использования каких-либо флагов компилятора. Я не уверен, почему здесь есть разница между языками, так как AFAIK, и C и C ++ имеют неопределенное поведение, если вы действительно пишете в строковый литерал.

В C ++ (очевидно, так как C ++ 11) тип строкового литерала фактически определяется языком как const char[], тогда как в стандартном C это всегда был просто char [], в который вы не должны писать. См. Каков тип строковых литералов в C и C ++?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...