18

我看到了一个示例程序来演示递归,它看起来不应该工作但确实有效。逻辑很清楚,但为什么即使没有返回递归函数调用,它也能工作呢?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

4 回答 4

20

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

编辑:在现代架构上,函数的返回值可能是在特定的硬件寄存器中传递的。当您递归调用函数时,在所有情况下,硬件寄存器的底部都设置为期望值。如果偶然从递归中弹出硬件寄存器从未更改过,那么您最终会得到正确的值。

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

无论如何,所有这些都应该被任何现代编译器捕获并给你一个警告。如果不是,则说明您没有一个好的编译器,或者您使用了过于防御性的命令行选项。

除夕特别:在现实世界中,像这样(带有return)的代码甚至不会被实现为递归函数。无需太多努力,您就会找到该函数的迭代变体,如果您要求最大优化,任何现代体面的编译器都应该能够找到它。

于 2012-12-31T14:28:47.270 回答
5

这里很大程度上取决于您所说的“它有效”是什么意思?

为了尝试回答您问题的要点,无论是否满足 return 语句,函数都会在到达函数末尾时返回。

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

我会说这个示例“有效”,因为在找到素数并且 isPrime 返回之后,堆栈中的下一个函数也可以自由返回。也不依赖于 isPrime 的返回值,因此程序将运行备份堆栈并输出一些东西。

...但由于行为未定义,实际获得输出的值可能是垃圾。如果您看到 0 和 1 与素数一致作为输入,那么哇。

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

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

于 2012-12-31T14:47:31.193 回答
2

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

该函数被调用,它递归回到自身,直到它在模数(返回 0)或递归结束(返回 1)处返回。此时函数返回给调用者,即 is_prime。但是函数中没有更多代码可以执行,所以它立即返回,没有任何进一步的操作。

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

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

于 2012-12-31T15:08:53.337 回答
-3

你不会忘记退货声明吗?对于正常递归,您还需要在前面加上一个 return isprime(num,i-1);

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

于 2012-12-31T14:26:52.027 回答