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

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


当前回答

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

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

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

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

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

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

这种做法对我们很有效。

其他回答

一些警告可能意味着代码中可能出现语义错误或可能出现UB。例如;if()之后,一个未使用的变量,一个被局部变量掩盖的全局变量,或者有符号和无符号的比较。许多警告与编译器中的静态代码分析器或在编译时检测到的违反ISO标准有关,这“需要诊断”。虽然在特定情况下,这些事件可能是合法的,但大多数情况下,它们是设计问题的结果。

一些编译器,例如GCC,有一个命令行选项来激活“警告为错误”模式。这是一个很好的工具,如果残酷,教育新手。

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

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

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

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

非固定的警告迟早会导致代码中的错误。


例如,调试分段错误需要程序员追踪错误的根源(原因),它通常位于代码中比最终导致分段错误的行更前面的位置。

很典型的情况是,导致错误的行是编译器发出警告而你忽略的行,而导致分段错误的行是最终抛出错误的行。

修复警告就等于修复了问题……一个经典的!

以上的演示…考虑下面的代码:

#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使用时没有初始化,因此它有一个垃圾值,这导致索引字符串(方式)超出其界限,从而导致分割错误。

如果他们没有忽视这个警告,他们就会立即发现这个漏洞!

警告是等待发生的错误。 因此,您必须启用编译器警告,并整理代码以删除任何警告。