我经常听到有人说,在编译C和c++程序时,我应该“总是启用编译器警告”。为什么这是必要的?我怎么做呢?
有时我也听到我应该“把警告当作错误”。我应该吗?我怎么做呢?
我经常听到有人说,在编译C和c++程序时,我应该“总是启用编译器警告”。为什么这是必要的?我怎么做呢?
有时我也听到我应该“把警告当作错误”。我应该吗?我怎么做呢?
当前回答
其他的回答都很好,我不想重复他们说过的话。
One other aspect to "why enable warnings" that hasn't properly been touched on is that they help enormously with code maintenance. When you write a program of significant size, it becomes impossible to keep the whole thing in your head at once. You typically have a function or three that you're actively writing and thinking about, and perhaps a file or three on your screen that you can refer to, but the bulk of the program exists in the background somewhere and you have to trust that it keeps working.
如果你改变的某些东西给你看不见的东西带来了麻烦,你就会提醒自己。
例如Clang警告-Wswitch-enum。如果您在枚举上使用开关而漏掉了一个可能的枚举值,则会触发警告。您可能认为这是一个不太可能犯的错误:在编写switch语句时,您可能至少查看了枚举值列表。您甚至可能有一个IDE为您生成开关选项,不为人为错误留下任何空间。
六个月后,当您向枚举中添加另一个可能的条目时,这个警告才真正发挥作用。同样,如果您正在考虑所讨论的代码,那么您可能不会有问题。但是如果这个枚举用于多个不同的目的,并且它是用于您需要额外选项的其中一个目的,那么很容易忘记更新您六个月没有接触过的文件中的开关。
You can think of warnings in the same way as you'd think of automated test cases: they help you make sure that the code is sensible and doing what you need when you first write it, but they help even more to make sure that it keeps doing what you need while you prod at it. The difference is that test cases work very narrowly to the requirements of your code and you have to write them, while warnings work broadly to sensible standards for almost all code, and they're very generously supplied by the boffins who make the compilers.
其他回答
这是C的一个具体答案,以及为什么这对C来说比其他任何东西都重要。
#include <stdio.h>
int main()
{
FILE *fp = "some string";
}
此代码编译时带有警告。地球上几乎所有其他语言(除了汇编语言)中存在的和应该存在的错误都是C语言中的警告。C语言中的警告几乎都是伪装的错误。警告应该被修正,而不是被压制。
对于GCC,我们执行GCC -Wall -Werror。
这也是微软一些不安全API警告引起高度不满的原因。大多数编写C语言的人已经学会了将警告视为错误的艰难方法,而这些东西出现的不是同一种东西,需要不可移植的修复。
处理警告不仅能写出更好的代码,还能让你成为更好的程序员。警告会告诉你一些今天对你来说微不足道的事情,但总有一天坏习惯会回来咬你的头。
使用正确的类型,返回该值,计算该返回值。花点时间思考“在这种情况下,这真的是正确的类型吗?”“我需要把这个还回去吗?”最重要的是;“这个代码在未来10年里还能移植吗?”
首先要养成编写无警告代码的习惯。
c++编译器接受明显导致未定义行为的编译代码,这是编译器的一个主要缺陷。他们不修复这个问题的原因是,这样做可能会破坏一些可用的构建。
大多数警告应该是阻止构建完成的致命错误。默认情况下只显示错误并进行构建是错误的,如果您不覆盖它们,将警告视为错误,并留下一些警告,那么您可能会导致程序崩溃并做一些随机的事情。
为什么要启用警告?
众所周知,C和c++编译器在默认情况下不擅长报告一些常见的程序员错误,例如:
忘记初始化变量 忘记从函数返回值 printf和scanf族中的参数与格式字符串不匹配 函数的使用没有事先声明(仅限C语言)
这些可以被检测和报告,只是通常不是默认情况;此特性必须通过编译器选项显式地请求。
如何启用警告?
这取决于你的编译器。
微软C和c++编译器理解/W1、/W2、/W3、/W4和/Wall这样的开关。至少使用/W3。/W4和/Wall可能会对系统头文件发出错误的警告,但如果您的项目使用这些选项之一进行了干净的编译,那么就使用它。这些选择是相互排斥的。
大多数其他编译器都理解-Wall、-Wpedantic和-Wextra这样的选项。-Wall是必要的,其余的都是推荐的(注意,尽管它的名字,-Wall只启用最重要的警告,而不是全部)。这些选项可以单独使用,也可以一起使用。
您的IDE可能有办法从用户界面启用这些功能。
为什么我要把警告当作错误?它们只是警告!
A compiler warning signals a potentially serious problem in your code. The problems listed above are almost always fatal; others may or may not be, but you want compilation to fail even if it turns out to be a false alarm. Investigate each warning, find the root cause, and fix it. In the case of a false alarm, work around it — that is, use a different language feature or construct so that the warning is no longer triggered. If this proves to be very hard, disable that particular warning on a case by case basis.
你不希望只是把警告作为警告,即使它们都是假警报。对于发出的警告总数小于7的非常小的项目来说,这是可行的。再多一点,新的警告就很容易淹没在大量熟悉的旧警告中。不要允许这种情况发生。只是让你所有的项目编译干净。
Note this applies to program development. If you are releasing your project to the world in the source form, then it might be a good idea not to supply -Werror or equivalent in your released build script. People might try to build your project with a different version of the compiler, or with a different compiler altogether, which may have a different set of warnings enabled. You may want their build to succeed. It is still a good idea to keep the warnings enabled, so that people who see warning messages could send you bug reports or patches.
如何将警告视为错误?
这同样是通过编译器开关完成的。/WX是微软的,大多数其他人使用-Werror。在这两种情况下,如果产生任何警告,编译都会失败。
这就够了吗?
可能不是!随着优化级别的提高,编译器开始越来越仔细地检查代码,而这种仔细检查可能会发现更多的错误。因此,不要满足于警告开关本身,总是使用它们时,编译优化启用(-O2或-O3,或/O2如果使用MSVC)。
其他的回答都很好,我不想重复他们说过的话。
One other aspect to "why enable warnings" that hasn't properly been touched on is that they help enormously with code maintenance. When you write a program of significant size, it becomes impossible to keep the whole thing in your head at once. You typically have a function or three that you're actively writing and thinking about, and perhaps a file or three on your screen that you can refer to, but the bulk of the program exists in the background somewhere and you have to trust that it keeps working.
如果你改变的某些东西给你看不见的东西带来了麻烦,你就会提醒自己。
例如Clang警告-Wswitch-enum。如果您在枚举上使用开关而漏掉了一个可能的枚举值,则会触发警告。您可能认为这是一个不太可能犯的错误:在编写switch语句时,您可能至少查看了枚举值列表。您甚至可能有一个IDE为您生成开关选项,不为人为错误留下任何空间。
六个月后,当您向枚举中添加另一个可能的条目时,这个警告才真正发挥作用。同样,如果您正在考虑所讨论的代码,那么您可能不会有问题。但是如果这个枚举用于多个不同的目的,并且它是用于您需要额外选项的其中一个目的,那么很容易忘记更新您六个月没有接触过的文件中的开关。
You can think of warnings in the same way as you'd think of automated test cases: they help you make sure that the code is sensible and doing what you need when you first write it, but they help even more to make sure that it keeps doing what you need while you prod at it. The difference is that test cases work very narrowly to the requirements of your code and you have to write them, while warnings work broadly to sensible standards for almost all code, and they're very generously supplied by the boffins who make the compilers.