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

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


当前回答

将警告视为错误只有一个问题:当你使用来自其他来源的代码时(例如,微软库,开源项目),他们没有正确地完成他们的工作,编译他们的代码会产生大量的警告。

在编写代码时,我总是确保它不会产生任何警告或错误,并在编译之前进行清理,而不会产生任何无关的噪音。我不得不处理的垃圾让我感到震惊,当我不得不构建一个大项目时,看着一串警告从编译器应该只声明它处理了哪些文件的地方经过时,我感到震惊。

我也记录我的代码,因为我知道软件真正的生命周期成本主要来自维护,而不是最初的编写,但这是另一回事……

其他回答

处理警告不仅能写出更好的代码,还能让你成为更好的程序员。警告会告诉你一些今天对你来说微不足道的事情,但总有一天坏习惯会回来咬你的头。

使用正确的类型,返回该值,计算该返回值。花点时间思考“在这种情况下,这真的是正确的类型吗?”“我需要把这个还回去吗?”最重要的是;“这个代码在未来10年里还能移植吗?”

首先要养成编写无警告代码的习惯。

忽略警告意味着您留下了草率的代码,这不仅会在将来给其他人带来问题,而且还会使您不太注意到重要的编译消息。

编译器输出越多,就越不会有人注意到。越干净越好。这也意味着你知道自己在做什么。警告是非常不专业、粗心和危险的。

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

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

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

众所周知,就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代码的人,启用编译器警告有助于在提出修复时显示许多弱点和需要调查的领域。在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:在函数' 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:注意:阴影声明在这里

格式字符串

这在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 =)


这只是编译器可以为您进行双重检查的许多事情中的三件。还有很多其他的方法,比如使用未初始化的变量。