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

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


当前回答

这是C的一个具体答案,以及为什么这对C来说比其他任何东西都重要。

#include <stdio.h>

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

此代码编译时带有警告。地球上几乎所有其他语言(除了汇编语言)中存在的和应该存在的错误都是C语言中的警告。C语言中的警告几乎都是伪装的错误。警告应该被修正,而不是被压制。

对于GCC,我们执行GCC -Wall -Werror。

这也是微软一些不安全API警告引起高度不满的原因。大多数编写C语言的人已经学会了将警告视为错误的艰难方法,而这些东西出现的不是同一种东西,需要不可移植的修复。

其他回答

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

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

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

作为使用遗留嵌入式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 =)


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

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

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

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

别着急:你不必这么做,也没有必要。-Wall和-Werror是由代码重构狂人为自己设计的:它是由编译器开发人员发明的,目的是避免在用户端编译器或编程语言更新后破坏现有的构建。特性本身并不是什么,而是关于是否破坏构建的决定。

使用与否完全取决于您的喜好。我一直在用它,因为它能帮我改正错误。

编译器警告是你的朋友

我在传统的Fortran 77系统上工作。编译器告诉我有价值的东西:在子例程调用上的参数数据类型不匹配,如果我有一个变量或子例程参数没有被使用,那么在值被设置到变量之前使用一个局部变量。这些几乎都是错误。

当我的代码编译干净,97%的工作。与我一起工作的另一个人在编译时关闭了所有警告,在调试器中花费数小时或数天,然后让我帮忙。我只是用警告编译他的代码,然后告诉他要修改什么。