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

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

int badFun() { return badFun(); }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

如果你在Visual Studio安装像Resharper这样的代码分析器,它会带来无限递归调用的警告,或者像你启用代码分析选项那样。

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

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

正如您所提到的,编译器只是检查语法错误。 递归函数完全有效,没有任何错误。

在运行期间,

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

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