Недостатки использования флага `-Wextra` при компиляции в GCC

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

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

Я был очень удивлен, читая это. Таким образом, я начал заниматься поиском в Google, но я не получил никакого ответа, поскольку все результаты поиска показали, «что делает флаг -Wextra ?».

Итак, мои вопросы

  • В каких ситуациях флага -Wextra генерирует необработанные предупреждения?
  • Можно ли остановить флаг -Wextra от включения других флагов, которые заставляют GCC выдавать эти типы предупреждений?

    Вопрос о полезности -Wextra предупреждений заключается в том, что соответствующие предупреждения имеют эти свойства (более или менее):

    1. Они генерируют ложные срабатывания.

    2. Ложные срабатывания относительно часты.

    3. Адаптация кода во избежание ложных срабатываний является досадой.

    Следствием этого является то, что многие проекты, которые include -Wall -Wextra отказываются от попытки избежать всех предупреждений. Следовательно, когда вы компилируете такой проект, вы видите сотни и сотни предупреждений, почти все о законном коде. И одно предупреждение, что вы должны были заметить незаметно в бесконечном предупреждающем streamе. Хуже того: тот факт, что обычная компиляция выплескивает так много предупреждений, заставляет вас небрежно относиться к предупреждению о новых предупреждениях, которые вводит ваш новый код. Когда проект достигает нескольких десятков предупреждений, битва обычно заканчивается; для этого потребуется героическое усилие вернуть счетчик предупреждений к нулю.

    Вот пример некоторого совершенно законного кода, который -Wextra на -Wextra :

     void myClass_destruct(MyClass* me) {} 

    Это деструктор, и он пуст. Тем не менее, это должно быть просто, чтобы облегчить вызов его в соответствующих точках (деструкторы подclassа), так что легко добавить материал в MyClass который нуждается в очистке. Однако -Wextra будет лаять на неиспользуемый параметр. Это заставляет программистов писать такой код:

     void myClass_destruct(MyClass* me) { (void)me; //shut up the compiler barking about the unused parameter } 

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

    1. Чистый код

    2. Счетчик нулевого предупреждения

    3. -Wextra предупреждения

    Выберите мудро, какой из них вы хотите сбросить, вы не получите всех трех.

    Некоторые из -Wextra предупреждений обеспечивают соответствующий стиль кодирования, который может конфликтовать с некоторыми процессами людей. Это единственная причина, чтобы избежать этого. Моим решением в этом случае является «правильный» точный набор с использованием синтаксиса -Wno-* .

    Например, -Wextra подразумевает -Wempty-body которое предупреждает вас о пустых телах некоторых структур управления, подобных if , else и while . Но если вы будете следовать с одной стороны, итеративный стиль реализации, а другой – серьезное количество предупреждений, вам это будет неудобно.

    Вот пример разработки кода:

     void doSomething() { if (validatePreConditions()) { //TODO Handle it some time. } // ... Here you have real code. } 

    Если у вас есть -Werror , и вы хотите начать тестирование без предварительной проверки состояния, у вас проблемы.

    Но вот ловушка, то, что приемлемо для кода «in-development», должно быть исправлено с помощью временного кода. Поэтому мой вариант для таких случаев состоит в том, чтобы иметь разные предупреждения и разные профили сборки. Так, например, assembly «разработчика» в порядке, чтобы иметь пустые тела, но что-то, что собирается быть объединено в main-line, MUST NOT иметь такие пробелы.

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

    В каких ситуациях флага -Wextra генерирует необработанные предупреждения?

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

    Можно ли остановить флаг -Wextra от включения других флагов, которые заставляют GCC выдавать эти типы предупреждений?

    Stick -Wno- перед предупреждением, чтобы отключить его. Не все предупреждения имеют предупреждающий флаг, что означает, что вам, вероятно, придется придерживаться его. Вы также можете найти больше информации, просматривая их багтрекер , ища определенные флаги предупреждения и видя, существуют ли какие-либо обсуждения. Если он не находится в документации, он может быть там. Разработчики могут также объяснить, почему все так, как есть.


    Чтобы ответить на комментарий, который вы указали в комментариях,

    … или предупреждения о недостающих инициализаторах поля (когда вы намеренно инициализируете структуру с помощью {0}, делая компилятор нулевой инициализацией всего остального автоматом).

    Этот аргумент спор, потому что он уже исправлен . Например, следующий код не выдаёт никаких предупреждающих предупреждений об ошибках :

     typedef struct { int a; int b; } S; int main() { S s = { 0 }; } 

    Это очень просто;

    Бесполезное предупреждение – это тот, который никогда не указывает на реальную ошибку.

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

    Все предупреждения GCC когда-то или другие указывают на реальные ошибки, поэтому общая тема с сообщениями GCC заключается в том, что -Все предупреждения могут указывать на ошибки и их легко подавить, если они этого не делают. Так что это хорошо работает с -Werror.

    Сообщения -Wextra, OTOH указывают на реальные ошибки реже или могут указывать на конструкции, которые являются обычной практикой в ​​каком-то более старом коде или просто трудно изменить на альтернативный способ выражения кода. Поэтому иногда они могут быть «слишком многословными».

    Для нового кода вы обычно хотите -Wextra on.

    Для старого кода он должен быть включен, если он не «слишком многословный».

    Существуют три типа предупреждений, и GCC не очень хорошо разбирается в их группировании или делает это ясным:

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

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

       int i = 1; if (INT_MAX > 0x7fffffff) <<= 31; 
    3. Предупреждения, которые являются чисто эвристическими и сами по себе не указывают на какую-либо ошибку программирования или недействительность вашей программы, когда компилятор просто предупреждает вас, что вы, возможно, намеревались что-то отличное от того, что вы написали.

    -Wextra включает много предупреждений типа 3.

    Как правило, все согласны с типами 1 и 2, но, как упоминалось выше, даже тип 2 может иметь ложные срабатывания. Тип 3 обычно имеет множество ложных срабатываний; некоторые из них могут быть подавлены безвредными изменениями в коде, в то время как другие требуют на самом деле сделать ваш код хуже (или отключить предупреждение локально), чтобы избежать их.

    Здесь люди не согласятся. Для некоторых программистов вы просто включаете -Wall -Wextra и локально -Wall -Wextra или отключите предупреждения, в которых появляются ложные срабатывания. Если вы этого не сделаете, ложные срабатывания создают много шума и потенциально уменьшают отношение сигнал / шум, заставляя вас не замечать более серьезные предупреждения, если вы привыкли видеть предупреждения при компиляции.

    Есть (по крайней мере) два лагеря программистов C: те, которые считают, что компилятор иногда может знать лучше, и те, кто думает, кто компилятор, рассказывают мне, как писать свой код, в конце концов, я знаю, что я делаю.

    Camp 1 позволяет предупреждать столько, сколько они могут, использовать lint и другие проверки кода, чтобы даже получить больше подсказок о сомнительном или потенциально сломанном коде.

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

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

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

    В какой лагерь вы хотите принадлежать? От этого ответа зависят недостатки -Wextra . Недостатком одного человека является преимущество другого человека.