我经常听到有人说,在编译C和c++程序时,我应该“总是启用编译器警告”。为什么这是必要的?我怎么做呢?
有时我也听到我应该“把警告当作错误”。我应该吗?我怎么做呢?
我经常听到有人说,在编译C和c++程序时,我应该“总是启用编译器警告”。为什么这是必要的?我怎么做呢?
有时我也听到我应该“把警告当作错误”。我应该吗?我怎么做呢?
当前回答
别着急:你不必这么做,也没有必要。-Wall和-Werror是由代码重构狂人为自己设计的:它是由编译器开发人员发明的,目的是避免在用户端编译器或编程语言更新后破坏现有的构建。特性本身并不是什么,而是关于是否破坏构建的决定。
使用与否完全取决于您的喜好。我一直在用它,因为它能帮我改正错误。
其他回答
为什么要启用警告?
众所周知,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)。
处理警告不仅能写出更好的代码,还能让你成为更好的程序员。警告会告诉你一些今天对你来说微不足道的事情,但总有一天坏习惯会回来咬你的头。
使用正确的类型,返回该值,计算该返回值。花点时间思考“在这种情况下,这真的是正确的类型吗?”“我需要把这个还回去吗?”最重要的是;“这个代码在未来10年里还能移植吗?”
首先要养成编写无警告代码的习惯。
编译器警告是你的朋友
我在传统的Fortran 77系统上工作。编译器告诉我有价值的东西:在子例程调用上的参数数据类型不匹配,如果我有一个变量或子例程参数没有被使用,那么在值被设置到变量之前使用一个局部变量。这些几乎都是错误。
当我的代码编译干净,97%的工作。与我一起工作的另一个人在编译时关闭了所有警告,在调试器中花费数小时或数天,然后让我帮忙。我只是用警告编译他的代码,然后告诉他要修改什么。
众所周知,就hll而言,C是一种相当低级的语言。虽然c++看起来是一种比C高级得多的语言,但它仍然具有C的一些特征。其中一个特点就是这些语言是由程序员设计的,为程序员设计的——特别是那些知道自己在做什么的程序员。
(对于这个答案的其余部分,我将专注于C。我要说的大部分内容也适用于c++,尽管可能没有那么强烈。尽管Bjarne Stroustrup有一句名言:“C很容易搬起石头砸自己的脚;c++让它变得更难,但当你这样做的时候,它会把你的整条腿都炸掉。”)
如果你知道你在做什么——真的知道你在做什么——有时候你可能不得不“打破规则”。但大多数时候,我们大多数人都会同意,善意的规则让我们所有人都远离麻烦,一直肆意违反这些规则是一个坏主意。
But in C and C++, there are surprisingly large numbers of things you can do that are "bad ideas", but which aren't formally "against the rules". Sometimes they're a bad idea some of the time (but might be defensible other times); sometimes they're a bad idea virtually all of the time. But the tradition has always been not to warn about these things — because, again, the assumption is that programmers know what they are doing, they wouldn't be doing these things without a good reason, and they'd be annoyed by a bunch of unnecessary warnings.
当然,并不是所有的程序员都知道自己在做什么。特别是,每个C程序员(无论多么有经验)都要经历一个开始C程序员的阶段。即使是有经验的C程序员也会粗心大意,犯错误。
最后,经验表明,程序员不仅会犯错误,而且这些错误会产生真正的、严重的后果。如果你犯了一个错误,编译器没有警告你,而且程序没有立即崩溃,或者因为它而做一些明显的错误,这个错误可能潜伏在那里,隐藏,有时长达数年,直到它造成一个真正的大问题。
So it turns out that, most of the time, warnings are a good idea, after all. Even the experienced programmers have learned that (actually, it's "especially the experienced programmers have learned that"), on balance, the warnings tend to do more good than harm. For every time you did something wrong deliberately and the warning was a nuisance, there are probably at least ten times you did something wrong by accident and the warning saved you from further trouble. And most warnings can be disabled or worked around for those few times when you really want to do the "wrong" thing.
(这类“错误”的一个经典例子是if(A = b)的测试。大多数时候,这确实是一个错误,所以现在大多数编译器都会对此发出警告——有些甚至是默认的。但是如果你真的想把b赋值给a并测试结果,你可以通过输入if((a = b))来禁用警告。
The second question is, why would you want to ask the compiler to treat warnings as errors? I'd say it's because of human nature, specifically, the all-too-easy reaction of saying "Oh, that's just a warning, that's not so important, I'll clean that up later." But if you're a procrastinator (and I don't know about you, but I'm a world-class procrastinator) it's easy to put off the necessary cleanup for basically ever — and if you get into the habit of ignoring warnings, it gets easier and easier to miss an important warning message that's sitting there, unnoticed, in the midst of all the ones you're relentlessly ignoring.
因此,要求编译器将警告视为错误是你可以对自己玩的一个小技巧,以避免这个人类的弱点,迫使自己立即修复警告,否则你的程序将无法编译。
Personally, I'm not as insistent about treating warnings as errors — in fact, if I'm honest, I can say that I don't tend to enable that option in my "personal" programming. But you can be sure I've got that option enabled at work, where our style guide (which I wrote) mandates its use. And I would say — I suspect most professional programmers would say — that any shop that doesn't treat warnings as errors in C is behaving irresponsibly, is not adhering to commonly-accepted industry best practices.
非固定的警告迟早会导致代码中的错误。
例如,调试分段错误需要程序员追踪错误的根源(原因),它通常位于代码中比最终导致分段错误的行更前面的位置。
很典型的情况是,导致错误的行是编译器发出警告而你忽略的行,而导致分段错误的行是最终抛出错误的行。
修复警告就等于修复了问题……一个经典的!
以上的演示…考虑下面的代码:
#include <stdio.h>
int main(void) {
char* str = "Hello, World!!";
int idx;
// Colossal amount of code here, irrelevant to 'idx'
printf("%c\n", str[idx]);
return 0;
}
当使用传递给GCC的"Wextra"标志进行编译时,给出:
main.c: In function 'main':
main.c:9:21: warning: 'idx' is used uninitialized in this function [-Wuninitialized]
9 | printf("%c\n", str[idx]);
| ^
我可以忽略它,然后执行代码……然后我就会看到一个“重大”分割错误,就像我的IP伊壁鸠鲁教授曾经说过的那样:
段错误
为了在现实场景中调试这一点,人们将从导致分段错误的行开始,并试图跟踪原因的根源是什么……他们将不得不在那里的大量代码中搜索i和str发生了什么……
直到有一天,他们发现idx使用时没有初始化,因此它有一个垃圾值,这导致索引字符串(方式)超出其界限,从而导致分割错误。
如果他们没有忽视这个警告,他们就会立即发现这个漏洞!