为什么要始终启用编译器警告?


302

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

有时我还听说我应该“将警告视为错误”。我是不是该?我怎么做?

Answers:


341

为什么启用警告?

众所周知,C和C ++编译器默认情况下不善于报告一些常见的程序员错误,例如:

  • 忘记初始化变量
  • 忘记return函数中的值
  • printfscanf系列中的参数与格式字符串不匹配
  • 使用函数而无需事先声明(仅C)

这些可以被检测和报告,通常不是默认情况下;必须通过编译器选项显式请求此功能。

如何启用警告?

这取决于您的编译器。

微软的C和C ++编译器理解开关一样/W1/W2/W3/W4/Wall。至少使用/W3/W4/Wall可能对系统头文件发出虚假警告,但是如果您的项目使用这些选项之一进行了正确的编译,则应选择它。这些选项是互斥的。

大多数其他编译器理解像选项-Wall-Wpedantic-Wextra-Wall是必不可少的,所有其他建议都建议使用(请注意,尽管它的名称是,但-Wall仅启用最重要的警告,而不是全部)。这些选项可以单独使用,也可以一起使用。

您的IDE可能有一种从用户界面启用这些功能的方法。

为什么将警告视为错误?它们只是警告!

编译器警告表示您的代码中可能存在严重的问题。上面列出的问题几乎总是致命的。其他可能会或可能不会,但是您希望编译失败,即使事实证明这是一个错误的警报。研究每个警告,找到根本原因并解决。如果发生错误警报,请解决它-即,使用其他语言功能或结构,以便不再触发警告。如果事实证明这很困难,请逐个禁用该特定警告。

即使所有警告都是错误警报,您也不想将警告作为警告。对于很小的项目,发出的警告总数少于7个,这可能是可以的。不要这样 只要使您的所有项目都能干净地编译即可。

请注意,这适用于程序开发。如果您以源代码形式向世界发布项目,那么最好不要-Werror在已发布的构建脚本中提供或提供等效的版本。人们可能会尝试使用不同版本的编译器或完全不同的编译器来构建您的项目,这可能会启用一组不同的警告。您可能希望他们的建造成功。始终启用警告仍然是一个好主意,以便看到警告消息的人可以向您发送错误报告或补丁。

如何将警告视为错误?

再次使用编译器开关完成此操作。/WX适用于Microsoft,大多数其他人使用-Werror。无论哪种情况,如果产生任何警告,编译都会失败。


107
我之所以发布此问题与解答,是因为我厌倦了告诉人们启用警告。现在,我可以将他们指向此处(或者,如果我的心情特别邪恶,请以欺骗的方式结束他们的提问)。欢迎您改善此答案或添加您自己的答案!
n。代词

16
您还可以使用c的-Weverything
pmg

9
我要添加的唯一修饰符是某些警告可能对您的应用程序没有帮助。(我看到过警告,编译器在结构中的元素之间添加了2个字节的填充。应用程序用于原型设计,因此一点浪费的内存不会困扰我们。)将所有警告视为错误,然后仅在以下情况下禁用警告:您知道该警告为什么无济于事。
凯尔

20
将警告视为对遵循您的默认构建说明的人员的错误的不利之处是,随着编译器添加新警告,您的代码会腐烂。下载您的代码并在将来尝试构建它的用户可能无法这样做,因为他们的编译器太新了,并且会发出警告,指出一些额外的括号或您的编译器不关心的内容。遇到错误的用户不对您的代码或构建系统负责,也不知道如何关闭将警告视为错误并实际构建项目的方法。
interfect

15
@interfect是的,这在我身上发生了两次。没关系 如果您选择使用从未经过测试的编译器来构建不需要维护的软件,那么,最好准备自己进行一些维护
n。代词

94

众所周知,随着HLL的发展,C是一种相当底层的语言。尽管C ++似乎比C语言要高级得多,但它仍然具有许多特质。这些特征之一是这些语言是由程序员设计的,是为程序员而设计的,尤其是为知道自己在做什么的程序员设计的。

[在本答案的其余部分中,我将重点介绍C。我会说的大多数内容也适用于C ++,尽管可能不那么适用。尽管正如Bjarne Stroustrup所说的那样,“ C使脚上的射击变得容易; C ++使其更难,但是当您这样做时,它会使您的整个腿发疯。” ]

如果您知道自己在做什么- 真的知道自己在做什么-有时您可能不得不“违反规则”。但是大多数时候,我们大多数人都会同意,良好的规则会让我们所有人摆脱困境,并且大胆地始终违反这些规则是一个坏主意。

但是在C和C ++中,您可以做很多令人惊讶的事情,这些都是“坏主意”,但并不是形式上的“违背规则”。有时在某些情况下,这是个坏主意(但在其他时候可能是有道理的);有时,几乎所有时间他们都是一个坏主意。但是传统上一直不对这些事情发出警告-因为,再次假设,程序员知道他们在做什么,没有正当的理由他们就不会做这些事情,他们会被一堆烦恼不必要的警告。

但是当然,并不是所有的程序员都真正知道他们在做什么。尤其是,每个C程序员(无论经验如何)都经历了成为新手C程序员的阶段。甚至有经验的C程序员也会变得粗心和犯错。

最后,经验表明,不仅程序员确实会犯错误,而且这些错误可能会带来真正的严重后果。如果您犯了一个错误,并且编译器没有警告您,并且由于某种原因程序不会立即崩溃或执行明显错误的操作,那么该错误可能会在那里潜伏,隐藏,有时长达数年,直到导致一个真正的大问题。

因此,事实证明,在大多数情况下,警告毕竟是个好主意。即便是有经验的程序员也已经学会(实际上,“ 尤其是有经验的程序员已经学会”),总的来说,警告往往弊大于利。每次您故意做错了事而警告是令人讨厌的事,您至少有十次无意中做了错事,而警告使您免于进一步的麻烦。当您确实想做“错误”的事情时,大多数警告可以禁用或解决几次。

(这样的“错误”的一个典型的例子是测试if(a = b)在大多数情况下,这是一个错误,这几天警告它,大多数编译器- 。有些甚至默认但是,如果你真的想同时分配ba和试验结果,您可以通过键入来禁用警告if((a = b))。)

第二个问题是,为什么要让编译器将警告视为错误?我会说这是由于人的天性,特别是说“哦,那只是一个警告,不是那么重要,我稍后再整理”的反应太简单了。但是,如果您是拖延症患者(我不知道您是什么,但我是一个糟糕的拖延症患者),则很容易将必要的清理工作推迟到根本上-如果您养成了无视警告的习惯,在您忽略的所有警告消息中,越来越容易错过坐在那里的重要警告消息,而未被注意。

因此,要求编译器将警告视为错误,这是您可以解决这个人为错误的小技巧。

就我个人而言,我不太坚持将警告视为错误。(实际上,老实说,我可以说我实际上从未在“个人”编程中启用该选项。)但是,您可以确定我在工作时启用了该选项,在我们的样式指南中写道)要求其使用。我想说-我怀疑大多数专业的程序员都会说-任何不将警告视为C错误的车间都是不负责任的行为,没有遵循公认的行业最佳实践。


9
“知道他们在做什么的程序员”-大声笑;如果我见过一个“没有真正的苏格兰人”谬论:)
Dancrumb

3
@Dancrumb哈哈back atcha。我永远不确定我是否会理解苏格兰人的谬论,但我喜欢它,所以这对我来说是一个很好的练习。我猜这里的应用程序是这样的:“没有C程序员会写if(a = b),因此我们不需要警告它。” (然后某人会列出由该特定错误导致的10个发行产品中的10个严重错误的列表。)“好吧,没有经验丰富的 C程序员会写那个……”
Steve Summit

3
@SteveSummit但一个真正有经验的C程序员可以编写if (returnCodeFromFoo = foo(bar))和意思,以捕捉和在一个地方测试代码(假设唯一的目的foo就是有副作用!),一个事实真的有经验的程序员可以知道这是不是一个好的编码风格
不重要

5
问题是,最有经验的程序员会启用大多数(如果不是全部)警告。如果他们确实想要使用if (returnCodeFromFoo = foo(bar)),则可以添加注释并关闭警告(以便维护程序员在4年后查看该警告时,他/她将意识到该代码是故意的。也就是说,我工作了与(在Microsoft C ++领域中)坚持使用/ Wall并把警告视为错误的人相结合才是解决之道,嗯,不是(除非您想发表很多禁止意见)
Flydog57

3
至于谁做的人不是每天都在写代码,但是当我它往往是裸露的金属(通常是我自己设计的板),我觉得非常宝贵的警告。当将值填充到内部寄存器中时(以DMA描述符位置为例),有关转换为指针的警告意味着我进行了强制转换以清除警告。不会有错误,但是如果其他人(甚至我自己!)在几个月内拿起该代码,那很可能会造成混乱。此外,我还将无警告规则应用于我的CAD工具的输出。
彼得·史密斯

39

警告包含一些最熟练的C ++开发人员可以融入应用程序的最佳建议。他们值得保持。

C ++是一种图灵完整的语言,在很多情况下,编译器必须简单地相信您知道自己在做什么。但是,在许多情况下,编译器可以意识到您可能不打算编写自己编写的内容。一个典型的例子是与参数不匹配的printf()代码,或者传递给printf的std :: strings(这对我来说从来没有!)。在这些情况下,您编写的代码不是错误。这是一个有效的C ++表达式,带有可让编译器执行的有效解释。但是编译器有很强的直觉,您只是忽略了现代编译器易于检测的内容。这些是警告。对于编译器而言,它们是显而易见的事情,您可能会忽略使用C ++的所有严格规则。

关闭或忽略警告就像选择忽略那些比您熟练的人提供的免费建议。这是在huberis中的一课,当您飞得太靠近太阳并且机翼融化或发生内存损坏错误时,该课程就会结束。在这两者之间,我每天都会从天上掉下来!

“将警告视为错误”是这种理念的极端版本​​。这里的想法是,您解决编译器给您的每条警告-您听取每条免费建议,并采取相应措施。对于您来说,这是否是一个好的开发模型取决于团队和您正在开发哪种产品。这是和尚可能会采取的禁欲态度。对于某些人来说,它的效果很好。对于其他人则不是。

在我的许多应用程序中,我们都不将警告视为错误。我们这样做是因为这些特定的应用程序需要在具有不同年龄的多个编译器的多个平台上进行编译。有时,我们发现实际上不可能在不将警告转变为另一平台上的警告的情况下将警告固定在一侧。所以我们只是小心。我们尊重警告,但我们不会向后弯腰。


11
图灵完善的C ++与此有关。许多语言正在完善,如果您做错了事,请不要相信您....
再见SE

3
@KamiKaze每种语言都会有惯用的错误(例如Java不能阻止您编写不一致的equals/ hashCode),并且报告了其中的一种实现质量。
卡莱斯(Caleth),

2
@KamiKaze图灵完整性位用于表明在某些情况下编译器无法证明您的代码将无法按计划工作。这很重要,因为编译器无法使所有“错误”的代码都变成错误。错误只能保留给语言设计者确定总是“错误”的行为。(通常是因为它导致不一致的路径)。
Cort Ammon

2
这也指出了“所有警告都是错误”的挑战。从设计上讲,警告是更多机会,会触发一些可能正确的代码,以换取更频繁地触发错误的代码。将警告作为错误会导致您无法使用全部语言的功能。
Cort Ammon

2
但是,总的来说,程序员想要一种比“安全”功能更多的语言。我们需要一种语言来完成我们认为要执行的操作。因此,警告仍然很重要,因为我们希望计算机执行的实际操作是语义类。编译器可以通过定义“不正确”或“不安全”来选择它,但最后您仍然拥有程序员希望程序执行的行为的超类。警告有助于缩小超类的范围。
科特·阿蒙

19

处理警告不仅使代码更好,而且使您成为更好的程序员。警告会告诉您今天对您来说似乎很少的事情,但是有一天,这种坏习惯会再次出现,使您无法接受。

使用正确的类型,返回该值,评估该返回值。花点时间思考一下“在这种情况下,这真的是正确的类型吗?” “我需要退货吗?” 还有大个子 “此代码在未来10年内是否可以移植?”

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


17

其他答案非常好,我不想重复他们所说的话。

尚未正确提及的“为什么启用警告”的另一方面是,它们对代码维护有很大帮助。当您编写一个很大的程序时,就不可能一次把整个事情都牢记在心。通常,您有一个正在积极编写和考虑的功能,或者一个可能在屏幕上引用的文件或三个功能,但是该程序的大部分存在于后台的某个地方,因此您必须相信它继续工作。

发出警告,并使其充满活力,并尽可能多地出现在您的脸上,有助于警告您是否更改了某些内容而导致看不到的麻烦。

以clang警告为例-Wswitch-enum。如果您在枚举上使用开关并且错过了可能的枚举值之一,则会触发警告。您可能会认为这是一个不太可能犯的错误:您可能至少在编写switch语句时查看了枚举值列表。您甚至可能拥有一个为您生成切换选项的IDE,不留任何人为错误的余地。

六个月后,当您向枚举添加另一个可能的条目时,此警告真正产生了。同样,如果您正在考虑所涉及的代码,则可能会很好。但是,如果此枚举用于多种不同目的,并且是您需要额外选择的目的之一,那么很容易忘记更新6个月未触及的文件中的开关。

您可以以与自动化测试用例相同的方式来考虑警告:它们可以帮助您确保代码合理,并在您首次编写代码时就做您需要的事情,但是它们可以帮助您确保对代码的理解。在生产时不断做您需要的事情。区别在于,测试用例仅适用于您的代码要求,而您必须编写它们,而警告则几乎适用于几乎所有代码的明智标准,并且警告由编写编译器的boffins慷慨地提供。


9
他们帮助维护的另一种方式是,当您查看别人的代码时,无法分辨副作用是否是故意的。启用警告后,您知道他们至少知道此问题。
罗宾·贝内特

或者在我的情况下,您从嵌入式系统中导入一个文件,该文件包含一个具有数千个值的枚举的3000+行切换语句。“掉落”警告(通过使用goto避免了)掩盖了许多“未处理”的错误……嵌入式编译器没有发出任何一个,但是这些错误仍然很重要。
莫兹

15

不固定的警告迟早导致代码中的错误


例如,调试分段故障需要程序员跟踪故障的根源(原因),该故障根源通常位于代码中比最终导致分段故障的行更靠前的位置。

非常典型的原因是编译器发出了您忽略的警告的行,而导致分段错误的行是最终引发错误的行。

解决警告会解决问题。.经典!

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

#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 Epicurus教授曾经说过的那样:

分段故障

为了在真实的场景中调试这个,一会从引起分段错误行开始,并试图追查是什么原因的根源。他们将不得不寻找发生了什么事i,并str认为巨大的金额内那里的代码...

直到有一天,他们发现自己处于idx未初始化使用的情况,因此它具有垃圾值,这导致对字符串(方式)的索引超出其范围,从而导致分段错误。

如果只有他们没有忽略警告,他们将立即发现该错误!


4
以您的头衔:不一定。例如,一条警告建议在实际上不需要括号的公式中使用括号,它指出了永远不会导致错误的非问题。给定编程语言中的运算符优先级不变。曾经
Marc van Leeuwen

4
@MarcvanLeeuwen您引用的实例可能会出错,例如,如果不记得运算符优先级的程序员正确地修改了公式。该警告告诉您:“某人有时可能不清楚,请添加一些括号以使其更清楚”。尽管必须同意原始帖子的标题并不总是正确的。
休伯特·贾西涅内基

^任何事情都可以变成错误。在部分带括号的代码中引入错误与在完全带括号的代码中引入错误一样容易。
吉姆·巴尔特

......顺便说一句,我有问题的,其第一反应为“噢,它从segfaulted调试器str[idx]是不是 “好了,这里是stridx定义`?
贾斯汀时间-恢复莫妮卡

2
如果遇到细分错误,您会很幸运。如果运气不太好,则可能是idx碰巧是您在测试中期望的值(如果期望值为0,则不太可能),并且实际上碰巧指向了一些敏感数据,这些数据在部署时永远不应打印。
celtschk

14

将警告视为错误只是自律的一种手段:您正在编译一个程序来测试该闪亮的新功能,但是直到修复了草率的零件之后,您才能这样做。没有Werror提供其他信息,只是非常明确地设置了优先级:

在解决现有代码中的问题之前,请勿添加新代码

真正重要的是心态,而不是工具。编译器诊断输出是一个工具。MISRA(用于嵌入式C)是另一种工具。使用哪一个无关紧要,但是可以说编译器警告是最容易获得的工具(只需设置一个标志),并且信噪比非常高。因此,没有理由使用它。

没有工具是绝对可靠的。如果您编写const float pi = 3.14;,大多数工具都不会告诉您定义的π精度不佳,这可能会导致问题。if(tmp < 42)即使众所周知,给变量赋予无意义的名称和使用幻数是在大型项目中造成灾难的一种方法,大多数工具也不会引起人们的注意。必须了解,您编写的任何“快速测试”代码都只是这样:一个测试,在继续执行其他任务之前必须正确进行测试,同时仍然要看到它的缺点。如果您按原样保留这些代码,则在花费两个月的时间添加新功能后进行调试将变得非常困难。

一旦进入正确的思维模式,就没有必要使用了Werror。将警告作为警告可以使您做出明智的决定,是继续运行您将要启动的调试会话还是有意义的,还是先中止它并首先修复警告。


3
clippy不管是好是坏,Rust 的掉毛工具实际上都会警告常量“ 3.14”。这实际上是docs中示例。但是,正如您可能从名称中猜测的那样,clippy以提供积极帮助而感到自豪。
emk

1
@emk感谢您的示例,也许我应该以永不言弃的方式重述我的答案。我并不是说要检查不精确的π值是不可能的,只是仅仅消除警告并不能保证良好的代码质量。
德米特里·格里戈里耶夫

作为错误警告您的一件事是,自动构建将失败,从而警告您出现了问题。自动化构建还允许自动掉毛(爆炸是3 ... 2 ... 1 .. :)
莫兹

8

作为使用旧式嵌入式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 =]


这些只是编译器可以为您仔细检查的许多事情中的三件事。还有许多其他方法,例如使用其他人指出的未初始化变量。


7
在嵌入式世界中,让我最担心的警告是“ possible loss of precision”和“ comparison between signed and unsigned”警告。我发现很难理解有多少“程序员”会忽略这些错误(实际上,我不确定自己为什么不是错误)
Mawg说,请恢复Monica

3
在后一种情况下,@ Mawg,我相信它不是错误的主要原因是结果sizeof是无符号的,但是默认的整数类型是有符号的。的sizeof结果类型,size_t被典型地用于任何有关类型的尺寸,诸如,例如,对准或阵列/容器元素计数,而在一般的整数旨在被用作“ int除非另有规定”。因此,考虑到有多少人被教导要使用int其容器进行迭代(与相比),将其int设为size_t错误会破坏一切。; P
贾斯汀时间-恢复莫妮卡

6

这是对C的特定答案,为什么这对C比对其他任何东西都重要。

#include <stdio.h>
int main()
{
   FILE *fp = "some string";
}

此代码编译时带有警告。C 语言中的警告几乎是地球上所有其他语言中的(应该是)错误(除非汇编语言)。C 语言中的警告几乎总是伪装成错误。警告应固定,而不是禁止显示。

gcc,我们这样做gcc -Wall -Werror

这也是某些MS非安全API警告的高度保证的原因。大多数使用C语言进行编程的人都已经学会了将警告视为错误的艰难方法,而这些东西似乎不是同一种东西,并且需要不可移植的修复程序。


5

编译器警告是您的朋友(不要大喊,强调要大写)。

我在旧版Fortran-77系统上工作。编译器告诉我一些有价值的事情:如果我有未使用的变量或子例程参数,则子例程调用中的参数数据类型不匹配,在将值设置为变量之前使用局部变量。这些几乎都是错误。

避免冗长的文章:当我的代码完全编译时,97%的代码可以工作。与我一起工作的另一个人在关闭所有警告的情况下进行编译,在调试器中花费了数小时或数天,然后请我提供帮助。我只是编译带有警告的代码并告诉他要解决的问题。


5

您应该始终启用编译器警告,因为编译器通常可以告诉您代码出了什么问题。为此,您将传递-Wall -Wextra给编译器。

您通常应该将警告视为错误,因为警告通常表示您的代码有问题。但是,忽略这些错误通常很容易。因此,将它们视为错误将导致构建失败,因此您不能忽略这些错误。要将警告视为错误,请传递-Werror给编译器。


4

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

1-它可以向您显示可能在哪里犯了错误,谁会影响您的最终操作结果。例如,如果您没有初始化变量,或者如果您放置了“ =”而不是“ ==”(仅示例)

2-还可以向您显示代码不符合c ++标准的位置。这很有用,因为如果代码符合实际标准,则很容易将代码移至其他平台。

通常,警告非常有用,可以向您显示代码中哪些地方有错误,这些错误会影响算法的结果或在用户使用程序时防止某些错误。


4

忽略警告意味着您留下了草率的代码,这些代码不仅将来可能会给其他人造成问题,而且还会使重要的编译消息不被您注意到。编译器输出越多,任何人注意或烦扰的次数就越少。清洁越好。这也意味着您知道自己在做什么。警告非常不专业,粗心且有风险。


4

我曾经在一家生产电子测试设备的大型公司(《财富》 50强)工作。

我小组的核心产品是MFC程序,这些程序多年来产生了数以百计的警告。几乎在所有情况下都忽略了这些内容。

发生错误时,这简直是噩梦般的噩梦。

担任该职位后,我很幸运被聘为一家新公司的第一位开发人员。

我鼓励所有版本都采用“不警告”政策,并且将编译器警告级别设置为非常嘈杂。

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

这种做法对我们来说效果很好。


1
第二。 #pragma warning不仅抑制警告,它还具有双重目的,即可以与其他程序员快速交流某些有意而非偶然的信息,并充当搜索标签,以便在某些内容出现故障时快速查找可能有问题的区域,但不能纠正错误/警告。修理它。
贾斯汀时间-恢复莫妮卡

您说得对,贾斯汀(Justin),那正是我如何看待#pragma警告
Jim in Texas

3

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


3

将警告视为错误只有一个问题:当您使用来自其他来源的代码(例如,micro $ ** t库,开放源代码项目)时,它们没有正确执行工作,并且编译其代码会产生大量错误警告。

总是写我的代码,因此它不会生成任何警告或错误,并对其进行清理,直到其编译时不会产生任何外来的噪音。我必须与之打交道的垃圾使我感到震惊,当我不得不构建一个大项目并看着警告流经过编译应该只宣布它处理的文件的地方时,我感到惊讶。

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


不要敲门,对于那些可以向客户大声朗读编译器警告的人来说,咨询工作中有很多钱。
莫兹

其他来源的代码不会生成警告 一定意味着作者草率。这也可能意味着他们使用产生不同警告集合的不同编译器来编译代码。代码可以在一个编译器上进行编译而不会发出警告,而在另一个编译器上生成警告。也许只是一组不同的警告选项;例如他们使用了-Wall而你使用-Wall -Wextra
celtschk

2

某些警告可能意味着代码中可能存在语义错误或可能的UB。例如,;if(),未使用的变量,局部变量掩盖的全局变量之后,或者有符号和无符号的比较之后。许多警告与编译器中的静态代码分析器有关,或与编译时可检测到的违反ISO标准有关,这些警告“要求诊断”。尽管这些情况在特定情况下可能是合法的,但大多数情况下都是由于设计问题造成的。

某些编译器(例如gcc)具有命令行选项来激活“警告为错误”模式,这是一个不错的工具,即使是残酷的教育新手程序员。


1

该C ++编译器接受编译代码的事实,显然会导致不确定的行为在所有在编译器的一大缺陷。他们不解决此问题的原因是因为这样做可能会破坏某些可用的版本。

大多数警告应该是致命错误,它们会阻止构建完成。仅显示错误并进行构建的默认设置是错误的,如果您不覆盖它们以将警告视为错误并留下一些警告,则可能会导致程序崩溃并随机执行操作。


具有讽刺意味的是,许多未定义的行为实际上并不会引起警告,但是会默默地编译成讨厌的定时炸弹。; P
贾斯汀时间-恢复莫妮卡

1
问题是,如果标准要求提供错误消息,则在发生问题的所有情况下必须发出该错误消息,但是如果没有发生问题,则永远不会发出该错误消息。但是在未定义行为的情况下,可能无法决定。:例如,请考虑下面的代码int i; if (fun1()) i=2; if (fun2()) i=3; char s="abcde"[i];当且仅当这两个代码展品不确定的行为fun1(),并fun2()可以返回false上同样功能的执行。可能正确或不正确,但是编译器如何分辨?
celtschk

-2

您绝对应该启用编译器警告,因为某些编译器不善于报告一些常见的编程错误,包括以下内容:

->初始化变量被遗忘->从函数返回值被遗漏-> printf和scanf系列中的简单参数与不使用函数而无需事先声明的格式字符串不匹配,尽管仅在c中发生

因此,由于可以检测和报告这些功能,因此通常默认情况下不会;因此必须通过编译器选项明确请求此功能。


-32

放轻松:您不必,没有必要。-Wall和-Werror是由代码重构狂为自己设计的:它是由编译器开发人员发明的,以避免在编译器在用户端更新后破坏现有的版本。该功能什么都不是,而是决定是否中断构建的全部内容。

是否使用它完全取决于您的偏好。我一直使用它,因为它可以帮助我纠正错误。


19
尽管不是强制性的,但强烈建议使用它们
Spikatrix

18
-Wall and -Werror was designed by code-refactoring maniacs for themselves.[需要引用]
YSC

22
看来您在矛盾自己。如果您“一直使用它,因为它有助于解决[您的]错误”,难道不应该教给新的程序员,这样他们就可以从头做起。我不认为这个问题在询问是否可以在不使用-Walland 的情况下进行编译,而-Werror只是在询问是否是一个好主意。从您的最后一句话来看,听起来像是您在说。
scohe001

12
当您获得更多有关维护非您编写的代码的经验时,请重新访问此答案。
托尔比约恩Ravn的安徒生

这不是有用的答案。OP的问题中有4个问号。此回复回答多少?
安德斯(Anders)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.