我经常听到有人说,在编译C和c++程序时,我应该“总是启用编译器警告”。为什么这是必要的?我怎么做呢?
有时我也听到我应该“把警告当作错误”。我应该吗?我怎么做呢?
我经常听到有人说,在编译C和c++程序时,我应该“总是启用编译器警告”。为什么这是必要的?我怎么做呢?
有时我也听到我应该“把警告当作错误”。我应该吗?我怎么做呢?
当前回答
非固定的警告迟早会导致代码中的错误。
例如,调试分段错误需要程序员追踪错误的根源(原因),它通常位于代码中比最终导致分段错误的行更前面的位置。
很典型的情况是,导致错误的行是编译器发出警告而你忽略的行,而导致分段错误的行是最终抛出错误的行。
修复警告就等于修复了问题……一个经典的!
以上的演示…考虑下面的代码:
#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使用时没有初始化,因此它有一个垃圾值,这导致索引字符串(方式)超出其界限,从而导致分割错误。
如果他们没有忽视这个警告,他们就会立即发现这个漏洞!
其他回答
一些警告可能意味着代码中可能出现语义错误或可能出现UB。例如;if()之后,一个未使用的变量,一个被局部变量掩盖的全局变量,或者有符号和无符号的比较。许多警告与编译器中的静态代码分析器或在编译时检测到的违反ISO标准有关,这“需要诊断”。虽然在特定情况下,这些事件可能是合法的,但大多数情况下,它们是设计问题的结果。
一些编译器,例如GCC,有一个命令行选项来激活“警告为错误”模式。这是一个很好的工具,如果残酷,教育新手。
由于某些原因,c++中的编译器警告非常有用。
它可以告诉你,你可能在哪里犯了一个错误,这可能会影响你的操作的最终结果。例如,如果你没有初始化一个变量,或者如果你使用“=”而不是“==”(这只是例子) 它还允许向你展示你的代码不符合c++标准的地方。这很有用,因为如果代码符合实际标准,那么将很容易将代码移到其他平台。
一般来说,警告是非常有用的,可以告诉您代码中哪里有错误,这些错误可能会影响算法的结果,或者在用户使用您的程序时防止出现某些错误。
将警告视为错误只有一个问题:当你使用来自其他来源的代码时(例如,微软库,开源项目),他们没有正确地完成他们的工作,编译他们的代码会产生大量的警告。
在编写代码时,我总是确保它不会产生任何警告或错误,并在编译之前进行清理,而不会产生任何无关的噪音。我不得不处理的垃圾让我感到震惊,当我不得不构建一个大项目时,看着一串警告从编译器应该只声明它处理了哪些文件的地方经过时,我感到震惊。
我也记录我的代码,因为我知道软件真正的生命周期成本主要来自维护,而不是最初的编写,但这是另一回事……
我曾经在一家制造电子测试设备的大公司(财富50强)工作过。
我的团队的核心产品是一个MFC程序,多年来,它产生了数百个警告。在几乎所有的案例中都被忽略了。
当出现bug时,这简直是一场噩梦。
在那个职位之后,我很幸运地被一家新创业公司聘为第一个开发人员。
我鼓励所有构建都采用“无警告”策略,并将编译器警告级别设置为相当吵闹的级别。
我们的做法是使用#pragma warning - push/disable/pop用于开发人员确定确实没问题的代码,并在调试级别使用日志语句,以防万一。
这种做法对我们很有效。
c++编译器接受明显导致未定义行为的编译代码,这是编译器的一个主要缺陷。他们不修复这个问题的原因是,这样做可能会破坏一些可用的构建。
大多数警告应该是阻止构建完成的致命错误。默认情况下只显示错误并进行构建是错误的,如果您不覆盖它们,将警告视为错误,并留下一些警告,那么您可能会导致程序崩溃并做一些随机的事情。