为什么在没有显式return语句的情况下,递归的返回调用会突破堆栈?

时间:2012-12-31 14:23:59

标签: c++ recursion

我看到了一个示例程序,用于演示递归,看起来它应该不起作用,但确实如此。逻辑很清楚,但为什么即使没有返回递归的函数调用它也能工作?即使没有请求,return命令似乎也会突破堆栈。这是语言标准还是gcc的东西?我在Windows和Linux上使用gcc编译的C和C ++看到了它。

#include <iostream>
#include <cstdlib>

using namespace std;

int isprime(int num, int i)
{
   if (i == 1) {
      return 1;
   }
   else {
      if (num % i == 0)
         return 0;
      else
         isprime(num, i-1); // should be returned
   }
}


int main(int argc, char** argv)
{
   int input = atoi(argv[1]);
   cout << input << "\t" << isprime(input, input/2) << "\n";
}

4 个答案:

答案 0 :(得分:20)

这样的事情只有在意外地返回值恰好在调用者期望的寄存器中才有效。这仅在编译器将其作为递归函数实现时才有效。技术上未定义的行为使用不提供的函数的返回值。

编辑:在现代体系结构中,函数的返回值可以在特定的硬件寄存器中传递。当您以递归方式调用函数时,在所有情况下,硬件寄存器都设置为期望值。如果偶然从递归中弹出硬件寄存器永远不会改变,那么最终会得到正确的值。

如果返回值放在(递归)调用者堆栈的某个位置,则所有这种模式都不起作用。

在任何情况下,所有这些都应该由任何现代编译器捕获并给出警告。如果没有,你没有一个好的编译器,或者你使用的是防御性的命令行选项。

新年前夕特别:在现实世界中,像这样的代码(return)甚至不会被实现为递归函数。通过不太费力的努力,您将找到该函数的迭代变体,如果您要求最大化优化,任何现代正常的编译器都应该能够找到它。

答案 1 :(得分:5)

这里有很多取决于你的意思“它有效”吗?

尝试回答问题的要点,函数将在函数结束时返回,无论是否满足return语句。

我希望看到编译器警告告诉你可能的控件路径可能无法返回一个值,无论如何在C ++中。导致未定义的行为,请参阅此问题: not returning a value from a non-void returning function

我会说这个例子“工作”,因为在找到一个素数并且isPrime已经返回之后,然后堆栈的下一个函数也可以自由返回。没有任何东西依赖于isPrime的返回值,所以程序将运行堆栈并输出一些东西。

...但由于行为未定义,实际获得输出的值可能是垃圾。如果你看到0&amp; 1与素数一致作为输入,然后哇。

如果您认为这是有效的,我会考虑更广泛地使用不同的值进行测试。

您是否还在使用任何“调试”设置进行构建?如果是这样再次尝试使用调试设置关闭,因为有时会做额外的工作来保持未初始化的内存清洁。

答案 2 :(得分:2)

我可以准确地解释会发生什么:

调用该函数,并将其递归回自身,直到它以模数(返回0)或递归结束(返回1)到达返回值。此时,函数重新调整到调用者,即is_prime。但是函数中没有更多的代码可以执行,所以它会立即返回而不需要任何进一步的操作。

但是,您可以轻松地通过例如在is_prime()的调用后添加printf("Done for %d, %d\n", num, i);来解决这个问题[不必在if语句中]。或者添加在函数的进入/退出时创建和销毁的C ++对象,作为另一个示例。

你很幸运,它有效。而且它非常脆弱且易于破解 - 使用不同的编译器(或使用不同的优化设置,或编译器的新版本,或其他一百万个东西)编译它,它可能会破坏。

答案 3 :(得分:-3)

你不忘记退货声明吗?对于正常递归,您还需要在isprime(num,i-1);之前返回。

我想如果你使用严格的规则编译它,甚至应该给出一个编译警告,因为函数必须总是返回一个int,现在它不会(至少如果你的编译器没有修复它)。