为什么这个明显的无限递归没有给出编译器警告?

10 投票
7 回答
4443 浏览
提问于 2025-04-17 09:41

几个月前,我需要修复一些代码,这段代码引发了一些问题。代码大致是这样的:

int badFun() { return badFun(); }

这显然会导致栈溢出,即使是在我使用的高级语言(SilkTest中的4Test)中。这段代码显然没有任何好处。问题的第一个迹象是在脚本执行完后出现的警告,但没有编译错误或警告。有趣的是,我尝试在C++、C#和Python中写出相同结构的程序,结果它们都能编译或解释通过,没有语法错误或警告,尽管在运行时都出现了错误。我在这些情况下甚至没有看到任何警告。为什么这段代码默认情况下不会被认为是一个潜在的问题呢?

补充:我尝试在这三种语言中写出等效的函数,所以我添加了这些函数标签。我更感兴趣的是,为什么像这样的代码能够通过而没有任何警告。如果需要,请重新标记。

7 个回答

3

任何编程语言的编译器都不知道它正在编译的代码具体是什么意思。虽然这段代码很傻,但它是有效的代码,所以编译器还是会把它编译成可以运行的程序。

22

为什么这默认情况下不被视为问题呢?

这个错误是运行时错误,而不是编译时错误。代码本身是完全有效的,只是做了一些愚蠢的事情。你展示的这个非常简单的例子确实可以被检测到,但如果情况稍微复杂一点,就很难检测了:

void evil() {
    if (somethingThatTurnsOutToAlwaysBeTrue)
        evil();
}

为了判断这是否是个问题,编译器需要尝试弄清楚这个条件是否总是为真。在一般情况下,我认为这和判断程序是否最终会停止一样,都是无法计算的(也就是说,这是一个证明无法计算的问题)。

38

事情是这样的:编译器的警告其实是一些功能。而功能需要投入,而投入是有限的。这个投入可以用钱来衡量,也可以用某人愿意花在开源项目上的时间来衡量,但我可以肯定的是,它是有限的。

所以我们必须合理安排这些投入。每花一个小时设计、实现、测试和调试一个功能,就意味着我们少了一个小时可以用来做其他事情。因此,我们在决定添加哪些功能时非常谨慎。

这对所有功能都是如此。警告有一些特别的考虑。一个警告必须关于具有以下特征的代码:

  • 合法。显然,代码必须是合法的;如果不合法,那就不是警告,而是错误。
  • 几乎肯定是错误的。一个警告如果是针对正确且理想的代码,那就是个糟糕的警告。(另外,如果代码正确的,应该有办法让这个警告消失。)
  • 不明显。警告应该告诉你一些微妙的错误,而不是显而易见的错误。
  • 可以分析的。有些警告是根本不可能的;比如说,要求编译器解决“停机问题”的警告是不会出现的,因为那是不可能的。
  • 不太可能被其他测试方式发现。

在你提到的具体例子中,我们看到一些条件是满足的。代码是合法的,而且几乎肯定是错误的。但它不明显吗?有人可以很容易地看出这段代码是无限递归;这个警告帮助不大。它可以分析吗?你给出的简单例子是可以的,但找到无界递归的问题相当于解决停机问题。它不太可能被其他测试方式发现吗?不可能。一旦你在测试用例中运行那段代码,你会得到一个异常,告诉你具体哪里出错了。

因此,发出这样的警告对我们来说没有价值。我们可以用这些投入去做更好的事情。

撰写回答