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

时间:2012-01-06 17:57:22

标签: c# c++ python compiler-construction recursion

许多个月前,我不得不修复一些导致一些问题的代码。代码看起来基本上是这样的:

int badFun() { return badFun(); }

这显然导致了堆栈溢出,即使在我正在使用的高级语言中(SilkTest中的4Test)。这段代码无法被视为有益。问题的第一个迹象是脚本完成后看到的警告,但没有编译错误或警告。奇怪的是,我尝试用C ++,C#和Python编写具有相同结构的程序,并且所有这些程序都编译/解释时没有语法错误或警告,即使在所有情况下都存在运行时错误。在任何这些情况下我都没有看到任何警告。为什么默认情况下这不是一个可能的问题?

编辑:我尝试在所有三种语言中编写该函数的等效函数,因此我添加了这些函数标记。我对代码这样的代码没有任何警告的整体原因更感兴趣。如有必要,请重新登记。

7 个答案:

答案 0 :(得分:38)

这是交易:编译器警告是功能。功能需要努力,努力是有限的数量。 (它可能以美元计算,或者可能以某人愿意给开源项目的小时数来衡量,但我保证,它是有限的。)

因此我们必须预算这项工作。我们花费在设计,实现,测试和调试功能上的每一个小时都是我们可以用来做其他事情的一小时。因此,我们非常谨慎地决定要添加哪些功能。

所有功能都是如此。警告还有其他特殊问题。警告必须是具有以下特征的代码:

  • 法律。显然,代码必须是合法的;如果它不合法,那么它首先不是警告,这是一个错误。
  • 几乎肯定是错的。警告警告您正确,理想的代码是一个坏警告。 (另外,如果代码 正确,则应该有一种方法来编写代码,使警告消失。)
  • Inobvious。警告应该告诉你哪些错误是微妙的,而不是明显的。
  • 适合分析。有些警告根本不可能;例如,要求编译器解决停止问题的警告不会发生,因为那是不可能的。
  • 不太可能被其他形式的测试所吸引。

在您的具体示例中,我们看到满足其中一些条件。代码是合法的,几乎肯定是错的。但它不明显吗?有人可以轻松查看代码并看到它是无限递归;警告没有多大帮助。是否适合分析?你给出的简单例子是,但找到无界递归的一般问题等同于解决暂停问题。是否不太可能被其他形式的测试所捕获?不。在您的测试用例中运行该代码的那一刻,您将获得一个例外,告诉您确切的错误。

因此,发出警告是不值得的。我们有更好的方式来支出这笔预算。

答案 1 :(得分:20)

  

为什么默认情况下这不是问题?

错误是运行时错误,而不是编译时错误。代码完全有效,它只是做一些愚蠢的事情。您展示的非常简单的案例当然可以被检测到,但是很多情况只会稍微复杂一些,很难被发现:

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

为了确定这是否是一个问题,编译器必须试图弄清楚条件是否总是为真。在一般情况下,我认为这比计算程序最终是否会停止(即provably not computable)更具可计算性。

答案 2 :(得分:3)

任何编程语言的编译器都不会对它编译的代码的语义有任何想法。这是有效的代码,虽然很愚蠢,所以它将被编译。

答案 3 :(得分:2)

编译器或解释器如何知道函数在做什么?编译器和解释器的范围是编译或解释语法代码 - 而不是解释代码的语义。

即使编译器确实检查了这一点,你在哪里画线?如果你有一个永远计算阶乘的递归函数怎么办?

答案 4 :(得分:1)

因为编译器不会检查这些东西。

如果您在Resharper中安装了Visual Studio之类的代码分析器,则会发出无限递归调用的警告,或者如果启用了代码分析选项,则会发出警告。

答案 5 :(得分:1)

我怀疑编译器可以在编译时检测到运行时现象(堆栈溢出)。有很多有效的案例可以调用函数内部的递归。但是编译器怎么能从递归的坏情况中知道好处呢?

除非它有一些附加的AI,否则我认为编译器不能检测好的和坏的递归之间的差异,这是程序员的工作。

答案 6 :(得分:0)

如前所述,编译器只检查语法错误。 递归函数完全有效,没有任何错误。

在运行时,

  

当堆栈溢出时,由于堆栈溢出 * 而引发错误,而不是因为代码 *。

递归函数非常有效,但在实现中我们需要在填充堆栈之前将条件检查放回到返回值。