我经常听到有人说,在编译C和c++程序时,我应该“总是启用编译器警告”。为什么这是必要的?我怎么做呢?

有时我也听到我应该“把警告当作错误”。我应该吗?我怎么做呢?


当前回答

我曾经在一家制造电子测试设备的大公司(财富50强)工作过。

我的团队的核心产品是一个MFC程序,多年来,它产生了数百个警告。在几乎所有的案例中都被忽略了。

当出现bug时,这简直是一场噩梦。

在那个职位之后,我很幸运地被一家新创业公司聘为第一个开发人员。

我鼓励所有构建都采用“无警告”策略,并将编译器警告级别设置为相当吵闹的级别。

我们的做法是使用#pragma warning - push/disable/pop用于开发人员确定确实没问题的代码,并在调试级别使用日志语句,以防万一。

这种做法对我们很有效。

其他回答

其他的回答都很好,我不想重复他们说过的话。

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.

众所周知,就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.

由于某些原因,c++中的编译器警告非常有用。

它可以告诉你,你可能在哪里犯了一个错误,这可能会影响你的操作的最终结果。例如,如果你没有初始化一个变量,或者如果你使用“=”而不是“==”(这只是例子) 它还允许向你展示你的代码不符合c++标准的地方。这很有用,因为如果代码符合实际标准,那么将很容易将代码移到其他平台。

一般来说,警告是非常有用的,可以告诉您代码中哪里有错误,这些错误可能会影响算法的结果,或者在用户使用您的程序时防止出现某些错误。

警告包含了一些最熟练的c++开发人员可以放入应用程序中的最佳建议。他们值得留在身边。

C++, being a Turing complete language, has plenty of cases where the compiler must simply trust that you knew what you are doing. However, there are many cases where the compiler can realize that you probably did not intend to write what you wrote. A classic example is printf() codes which don't match the arguments, or std::strings passed to printf (not that that ever happens to me!). In these cases, the code you wrote is not an error. It is a valid C++ expression with a valid interpretation for the compiler to act on. But the compiler has a strong hunch that you simply overlooked something which is easy for a modern compiler to detect. These are warnings. They are things that are obvious to a compiler, using all the strict rules of C++ at its disposal, that you might have overlooked.

关闭或忽略警告,就像选择忽略那些比你更有经验的人的免费建议。这是一个傲慢的教训,当你飞得离太阳太近,翅膀融化了,或者发生记忆损坏错误时,这个教训就结束了。在这两者之间,我愿意随时从天上掉下来!

“将警告视为错误”是这一哲学的极端版本。这里的想法是解决编译器给您的每个警告——您听取每个免费建议并执行它。这对你来说是否是一个好的开发模式取决于你的团队以及你所开发的产品类型。这是僧侣可能有的苦行方式。对一些人来说,效果很好。对另一些人来说,则不然。

在我的许多应用程序中,我们不将警告视为错误。我们这样做是因为这些特定的应用程序需要在多个平台上编译,使用多个不同年代的编译器。有时我们会发现,如果在一个平台上修复一个警告,而不将其转化为另一个平台上的警告,实际上是不可能的。所以我们只是小心行事。我们尊重警告,但我们不会为它们竭尽全力。

c++编译器接受明显导致未定义行为的编译代码,这是编译器的一个主要缺陷。他们不修复这个问题的原因是,这样做可能会破坏一些可用的构建。

大多数警告应该是阻止构建完成的致命错误。默认情况下只显示错误并进行构建是错误的,如果您不覆盖它们,将警告视为错误,并留下一些警告,那么您可能会导致程序崩溃并做一些随机的事情。