Я часто слышу, что при компиляции программ C и C ++ я должен «всегда включать предупреждения компилятора». Зачем это нужно? Как мне это сделать?

Иногда я также слышу, что надо «рассматривать предупреждения как ошибки». Нужно ли мне? Как мне это сделать?

Ответы (20)

Зачем включать предупреждения?

Компиляторы C и C ++, как известно, плохо сообщают о некоторых типичных ошибках программистов по умолчанию, например:

  • забывает инициализировать переменную
  • забыть вернуть значение из функции
  • аргументы в семействах printf и scanf не соответствуют строке формата
  • функция используется без предварительного объявления (только C)

Их можно обнаружить и сообщить о них, но обычно не по умолчанию; эта функция должна быть явно запрошена через параметры компилятора.

Как включить предупреждения?

Это зависит от вашего компилятора.

Компиляторы Microsoft C и C ++ понимают такие переключатели, как / W1, / W2, / W3, / W4 и / Стена. Используйте не менее / W3./ W4 и / Wall могут выдавать ложные предупреждения для файлов системных заголовков, но если ваш проект компилируется без ошибок с одним из этих параметров, сделайте это. Эти параметры являются взаимоисключающими.

Большинство других компиляторов понимают такие параметры, как -Wall, -Wpedantic и -Wextra.-Wall является обязательным, а все остальные рекомендуются (обратите внимание, что, несмотря на свое название, -Wall включает только самые важные предупреждения, а не все из них) . Эти параметры можно использовать по отдельности или все вместе.

В вашей среде IDE может быть способ включить их из пользовательского интерфейса.

Почему я должен рассматривать предупреждения как ошибки? Это просто предупреждения!

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

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

Обратите внимание, это относится к разработке программ. Если вы публикуете свой проект в исходной форме, то было бы неплохо не предоставлять -Werror или эквивалент в вашей выпущенной сборке. сценарий. Люди могут попытаться собрать ваш проект с другой версией компилятора или с другим компилятором в целом, что может иметь другой набор предупреждений. Возможно, вы захотите, чтобы их сборка была успешной. По-прежнему рекомендуется оставлять предупреждения включенными, чтобы люди, которые видят предупреждающие сообщения, могли отправлять вам отчеты об ошибках или исправления.

Как обрабатывать предупреждения как ошибки?

Это снова делается с помощью переключателей компилятора./ WX для Microsoft, большинство других используют -Werror. В любом случае компиляция завершится ошибкой, если появятся какие-либо предупреждения.

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

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

Некоторые предупреждения могут означать возможную семантическую ошибку в коде или возможную UB. Например.; после if (), неиспользуемая переменная, глобальная переменная, замаскированная локальной, или сравнение подписанных и беззнаковых. Многие предупреждения связаны со статическим анализатором кода в компиляторе или с нарушениями стандарта ISO, обнаруживаемыми во время компиляции, которые «требуют диагностики». Хотя такие случаи могут быть законными в одном конкретном случае, в большинстве случаев они будут результатом проблем проектирования.

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

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

(В оставшейся части этого ответа я сосредоточусь на C. Большая часть того, что я скажу, также применима к C ++, хотя, возможно, не так сильно. , "C позволяет легко выстрелить себе в ногу; C ++ усложняет задачу, но когда вы это делаете, вам отрывается вся нога.".)

Если вы знаете, что делаете - на самом деле знаете, что делаете - иногда вам, возможно, придется «нарушить правила». Но в большинстве случаев большинство из нас соглашается с тем, что правила, действующие из лучших побуждений, уберегут нас от неприятностей и что бессмысленное нарушение этих правил все время - плохая идея.

Но в C и C ++ есть удивительно большое количество вещей, которые вы можете делать, которые являются «плохими идеями», но которые формально не «противоречат правилам». Иногда они иногда являются плохой идеей (но в других случаях могут быть оправданы); иногда они - плохая идея практически все время. Но по традиции всегда было , а не, чтобы предупреждать об этих вещах - потому что, опять же, предполагается, что программисты знают, что они делают, они не стали бы делать это без уважительной причины, и они ' буду раздражаться кучей ненужных предупреждений.

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

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

Получается, что в большинстве случаев предупреждения - это все-таки хорошая идея. Даже опытные программисты усвоили (на самом деле, «особенно опытные программисты узнали»), что в целом предупреждения, как правило, приносят больше пользы, чем вреда. Каждый раз, когда вы сознательно делали что-то не так, и предупреждение было неприятным, вероятно, как минимум десять раз вы сделали что-то не так случайно, и предупреждение спасло вас от дальнейших неприятностей. И большинство предупреждений можно отключить или обойти в тех случаях, когда вы действительно хотите сделать "неправильный" поступок.

(Классическим примером такой «ошибки» является проверка if (a = b). В большинстве случаев это ошибка, поэтому большинство компиляторов в наши дни предупреждают об этом - некоторые даже по умолчанию.Но если вы действительно хотели назначить b как a и протестировать результат, вы можете отключить предупреждение, набрав if (( а = б)).)

Второй вопрос: зачем вам просить компилятор рассматривать предупреждения как ошибки? Я бы сказал, что это из-за человеческой природы, в частности, из-за слишком легкой реакции: «О, это просто предупреждение, это не так важно, я уберу это позже». Но если вы прокрастинатор (а я не знаю, как вы, но я ужасный прокрастинатор), легко отложить обязательную очистку практически навсегда - и если вы приобретете привычку игнорирования предупреждений, становится все проще и легче пропустить важное предупреждающее сообщение, которое незаметно стоит среди всех тех, которые вы игнорируете.

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

Лично я не так настаиваю на том, чтобы рассматривать предупреждения как ошибки. (На самом деле, если честно, я могу сказать, что я практически никогда не включаю эту опцию в моем «личном» программировании.) Но можете быть уверены, что я включил эту опцию на работе, где наше руководство по стилю (которое я написал) требует его использования. И я бы сказал - я подозреваю, что большинство профессиональных программистов сказали бы, - что любой магазин, который не рассматривает предупреждения как ошибки в C, ведет себя безответственно, не придерживается общепринятых передовых практик.

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

C ++, будучи полным по Тьюрингу языком, имеет множество случаев, когда компилятор должен просто доверять тому, что вы знаете, что делаете. Однако во многих случаях компилятор может понять, что вы, вероятно, не собирались писать то, что написали. Классический пример - коды printf (), которые не соответствуют аргументам, или std :: strings, переданные в printf (не то, что когда-либо бывает у меня!). В этих случаях написанный вами код не является ошибкой. Это допустимое выражение C ++ с допустимой интерпретацией для компилятора. Но у компилятора есть сильная догадка, что вы просто упустили из виду то, что современный компилятор легко обнаружит. Это предупреждения. Это вещи, которые очевидны для компилятора, использующего все имеющиеся в его распоряжении строгие правила C ++, которые вы могли упустить из виду.

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

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

Во многих моих приложениях мы не рассматриваем предупреждения как ошибки. Мы делаем это, потому что эти конкретные приложения должны компилироваться на нескольких платформах с несколькими компиляторами разного возраста. Иногда мы обнаруживаем, что на самом деле невозможно исправить предупреждение на одной стороне, не превратив его в предупреждение на другой платформе. Так что мы просто осторожны. Мы уважаем предупреждения, но не отказываемся от них.

Нефиксированные предупреждениярано или поздно, приведут к ошибкам в вашем коде.


Отладка ошибки сегментации , например, требует, чтобы программист отследил корень (причину) ошибки, которая обычно находится в более раннем месте вашего кода, чем строка, которая в конечном итоге вызвала сегментацию неисправность.

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

Исправление предупреждения приводит к устранению проблемы ... Классика!

Демонстрация вышесказанного ... Рассмотрим следующий код:

#include 

int main(void) {
  char* str = "Hello, World!!";
  int idx;

  // Colossal amount of code here, irrelevant to 'idx'

  printf("%c\n", str[idx]);

  return 0;
}

, который при компиляции с флагом «Wextra», переданным в GCC, дает:

main.c: В функции main:
main.c: 9: 21: предупреждение: 'idx' используется в этой функции без инициализации [-Wuninitialized]
    9 | printf ("% c \ n", str [idx]);
      | ^

который я мог бы игнорировать и выполнять код в любом случае ... И тогда я стал бы свидетелем "большой" ошибки сегментации, поскольку мой IP Epicurus профессор говорил:

Ошибка сегментации

Чтобы отладить это в реальном сценарии, нужно начать со строки, которая вызывает ошибку сегментации, и попытаться отследить, что является корнем причины ... Им придется искать, что случилось с i и str внутри этого колоссального количества кода ...

До тех пор, пока однажды они не оказались в ситуации, когда они обнаруживают, что idx используется неинициализированным, поэтому он имеет значение мусора, что приводит к индексации строки (пути) за пределами ее границ. , что приводит к ошибке сегментации.

Если бы только они не проигнорировали предупреждение, они бы сразу нашли ошибку!

Предупреждения компилятора в C ++ по некоторым причинам очень полезны.

  1. Позволяет показать вам, где вы могли допустить ошибку, которая может повлиять на конечный результат ваших операций. Например, если вы не инициализировали переменную или использовали «=» вместо «==» (это только примеры)

  2. Это позволяет также показать вам, где ваш код не соответствует стандарту C ++. Это полезно, потому что, если код соответствует фактическому стандарту, будет легко перенести код, например, на другую платформу.

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

Остальные ответы превосходны, и я не хочу повторять то, что они сказали.

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

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

Возьмем, к примеру, Clang warning -Wswitch-enum. Это вызывает предупреждение, если вы используете переключатель в перечислении и пропускаете одно из возможных значений перечисления. Вы можете подумать, что это маловероятная ошибка: вы, вероятно, хотя бы смотрели список значений перечисления, когда писали оператор switch. У вас даже может быть IDE, которая генерирует для вас параметры переключения, не оставляя места для человеческой ошибки.

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

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

Предупреждения компилятора - ваш друг

Я работаю на устаревших системах Fortran 77. Компилятор сообщает мне ценные вещи: несоответствие типа данных аргумента при вызове подпрограммы и использование локальной переменной до того, как значение было установлено в переменную, если у меня есть переменная или аргумент подпрограммы, которые не используются. Это почти всегда ошибки.

Когда мой код компилируется чисто, на 97% он работает. Другой парень, с которым я работаю, компилирует без всех предупреждений, проводит часы или дни в отладчике, а затем просит меня помочь. Я просто компилирую его код с предупреждениями и говорю ему, что исправить.

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

Чем больше выводит компилятор, тем меньше кто-то заметит или побеспокоит. Чем чище, тем лучше. Это также означает, что вы знаете, что делаете. Предупреждения очень непрофессиональны, небрежны и опасны.

Тот факт, что компиляторы C ++ допускают компиляцию кода, который явно приводит к неопределенному поведению вообще, является серьезным недостатком компиляторов. Причина, по которой они не исправляют это, заключается в том, что это может привести к поломке некоторых используемых сборок.

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

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

Используйте правильный тип, верните это значение, оцените это возвращаемое значение. Найдите время и подумайте: «Действительно ли это правильный тип в данном контексте?» "Мне нужно вернуть это?" И самое главное; «Будет ли этот код переносимым в следующие 10 лет?»

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

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

  • Инициализировать переменную, о которой забывают
  • Возвращает значение из пропущенной функции
  • Простые аргументы в семействах printf и scanf не соответствуют строке формата
  • Функция используется без предварительного объявления, хотя это происходит только в C

Так как эти функции могут быть обнаружены и сообщены, просто обычно не по умолчанию; поэтому эта функция должна быть явно запрошена через параметры компилятора.

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

Я всегда пишу свой код, чтобы он не генерировал никаких предупреждений или ошибок, и очищал его, пока он не скомпилировался, не создавая постороннего шума. Мусор, с которым мне приходится работать, ужасает меня, и я поражаюсь, когда мне приходится создавать большой проект и смотреть, как идет поток предупреждений, когда компиляция должна только объявлять, какие файлы были обработаны.

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

Это конкретный ответ на C, и почему это намного важнее для C, чем для чего-либо еще.

#include 

int main()
{
   FILE *fp = "some string";
}

Этот код компилируется с предупреждением . Каковы и должны быть ошибки почти во всех других языках на планете (за исключением ассемблера): предупреждения в C. Предупреждения в C почти всегда являются замаскированными ошибками. Предупреждения следует фиксировать, а не подавлять.

В GCC мы делаем это как gcc -Wall -Werror.

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

You should always enable compiler warnings because the compiler can often tell you what's wrong with your code. To do this, you pass -Wall -Wextra to the compiler.

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

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

Не добавляйте новый код, пока не исправите проблемы в существующем коде

На самом деле важен образ мышления, а не инструменты. Вывод диагностики компилятора - это инструмент.MISRA C (для встроенного C) - еще один инструмент. Неважно, какой из них вы используете, но, возможно, предупреждения компилятора - это самый простой инструмент, который вы можете получить (нужно установить всего один флаг), а отношение сигнал / шум очень высокое. Так что нет причин не использовать его.

Ни один инструмент не является безошибочным. Если вы напишете const float pi = 3.14;, большинство инструментов не сообщат вам, что вы определили π с плохой точностью, что может привести к проблемам в будущем. Большинство инструментов не вызовут удивления по поводу if (tmp <42), даже если общеизвестно, что присвоение переменных бессмысленных имен и использование магических чисел - это путь к катастрофе в больших проектах.Вы должны понимать, что любой код «быстрого тестирования», который вы пишете, - это всего лишь тест, и вы должны пройти его прямо, прежде чем переходить к другим задачам, пока вы все еще видите его недостатки. Если вы оставите этот код как есть, отладить его после того, как вы потратите два месяца на добавление новых функций, будет значительно сложнее.

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

Однажды я работал в крупной компании (Fortune 50), которая производила электронное испытательное оборудование.

Основным продуктом моей группы была программа MFC, которая на протяжении многих лет генерировала буквально сотни предупреждений. Которые игнорировались почти во всех случаях.

Когда возникают ошибки, это ужасный кошмар.

После этой должности мне посчастливилось быть нанятым первым разработчиком в новом стартапе.

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

Наша практика заключалась в использовании # pragma warning - push / disable / pop для кода, который был уверен, что разработчик действительно в порядке, вместе с оператором журнала на уровне отладки, на всякий случай.

Эта практика сработала для нас.

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

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

Оставленные переменные

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

int foo(int a, int b)
{
   int c = 0;

   if (a > 0)
   {
        return a;
   }
   return 0;
}

Простая компиляция без -Wall или -Wextra не возвращает никаких проблем.-Wall скажет вам, что c никогда не используется:

foo.c: In function ‘foo’:

foo.c: 9: 20: предупреждение: неиспользуемая переменная «c» [-Wunused-variable]

-Wextra также сообщит вам, что ваш параметр b ничего не делает:

foo.c: В функции «foo»:

foo.c: 9: 20: предупреждение: неиспользуемая переменная «c» [-Wunused-variable]

foo.c: 7: 20: предупреждение: неиспользуемый параметр ‘b’ [-Wunused-parameter] int foo (int a, int b)

Затенение глобальной переменной

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

int c = 7;

int foo(int a, int b)
{
   int c = a + b;
   return c;
}

Когда был включен -Wshadow, эту проблему легко обнаружить.

foo.c: 11: 9: предупреждение: объявление «c» затеняет глобальное объявление [-Wshadow]

foo.c: 1: 5: note: затененное объявление здесь

Строки формата

Это не требует каких-либо дополнительных флагов в GCC, но это все еще было источником проблем в прошлом. Простая функция, пытающаяся распечатать данные, но имеет ошибку форматирования, может выглядеть так:

void foo(const char * str)
{
    printf("str = %d\n", str);
}

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

foo.c: В функции «foo»:

foo.c: 10: 12: предупреждение: ожидается формат «% d» аргумент типа «int», но аргумент 2 имеет тип «const char *» [-Wformat =]


Это лишь три из множества вещей, которые компилятор может дважды проверить за вас. Есть много других, например, использование неинициализированной переменной, на которую указали другие.

2022 WebDevInsider