【问题标题】:Stack overflow caused by recursive function递归函数导致的堆栈溢出
【发布时间】:2013-04-05 06:35:06
【问题描述】:

我是 C++ 的初学者。昨天我读到了递归函数,所以我决定自己写。这是我写的:

int returnZero(int anyNumber) {
    if(anyNumber == 0)
        return 0;
    else  {
        anyNumber--;
        return returnZero(anyNumber);
    }
}

当我这样做时:int zero1 = returnZero(4793);,它会导致堆栈溢出。但是,如果我将值 4792 作为参数传递,则不会发生溢出。

关于为什么的任何想法?

【问题讨论】:

  • 也许更大的值正是溢出堆栈所需要的?
  • 尝试 5000 - 它很可能也会溢出堆栈。你的系统有多少内存?
  • 您是否在问为什么您的堆栈具有特定大小?

标签: c++ function recursion stack-overflow


【解决方案1】:

无论何时调用函数,包括递归调用,返回地址和参数通常都会被推送到call stack。堆栈是有限的,所以如果递归太深,你最终会耗尽堆栈空间。

让我吃惊的是,你的机器上只需要 4793 次调用就会溢出堆栈。这是一个很小的堆栈。相比之下,在我的计算机上运行相同的代码在程序崩溃之前需要大约 100 倍的调用次数。

堆栈的大小是可配置的。在 Unix 上,命令是ulimit -s

鉴于函数是tail-recursive,一些编译器可能会通过将递归调用转换为跳转来优化递归调用。一些编译器可能会更进一步地采用您的示例:当被要求进行最大优化时,gcc 4.7.2 将整个函数转换为:

int returnZero(int anyNumber) {
  return 0;
}

这需要两个汇编指令:

_returnZero:
        xorl    %eax, %eax
        ret

相当整洁。

【讨论】:

  • 他说它适用于 i = 4793,所以它应该适用于 i = 4792...还是不行?
  • 我认为一旦函数递归调用自身,由原始函数调用创建的堆栈帧就会从堆栈中弹出。不是这样吗?也感谢您的回复。
  • @charles:一般情况下,不可能。一个足够好的编译器将能够优化某些情况,例如尾递归:en.wikipedia.org/wiki/Tail_call
  • NPE - 啊太棒了。我永远不会猜到。最后一个问题,在每次递归调用时,新调用是创建一个新的堆栈帧还是只使用创建的原始堆栈帧?
  • @charles:新堆栈帧。就像任何其他函数调用一样。
【解决方案2】:

您刚刚达到了系统调用堆栈的大小限制,这就是正在发生的事情。由于某些原因,您系统中的堆栈很小,4793 个函数调用的深度相当小。

【讨论】:

    【解决方案3】:

    你的筹码量是有限的,所以当你打电话给4793 时,你已经达到了极限,而4792 刚好进来。每个函数调用都会使用堆栈上的一些空间来进行内务处理和参数。

    此页面提供了example 在递归函数调用期间堆栈的外观。

    【讨论】:

      【解决方案4】:

      我的猜测是你的堆栈足够大,可以容纳 4792 个条目 - 今天。明天或下一个,这个数字可能会有所不同。递归编程可能很危险,这个例子说明了原因。我们尽量不让递归变得这么深,否则可能会发生“糟糕”的事情。

      【讨论】:

        【解决方案5】:

        任何“无限”递归,即自然不限于小(ish)数字的递归调用都会产生这种效果。限制的确切位置取决于操作系统、调用函数的环境(编译器、哪个函数调用递归函数等)。

        如果您向调用递归函数的函数添加另一个变量,例如 int x[10];,则崩溃所需的数字将会改变(可能大约 5 左右)。

        用不同的编译器(甚至不同的编译器设置,例如打开优化)编译它,它可能会再次改变。

        【讨论】:

          【解决方案6】:

          使用递归,可以实现SuperDigit:

          public class SuperDigit
          {
              static int sum = 0;
          
              int main()
              {
                  int n = 8596854;
                  cout<<getSum(n);
              }
          
              int getSum(int n){
                  sum=0;
                  while (n > 0) {
                      int rem;
                      rem = n % 10;
                      sum = sum + rem;
                      n = n / 10;
                      getSum(n);
                  }
                  return sum;
              }
          }
          

          【讨论】:

            猜你喜欢
            • 2011-02-26
            • 2020-03-06
            • 2018-06-19
            • 1970-01-01
            • 1970-01-01
            • 2018-10-29
            相关资源
            最近更新 更多