由递归函数引起的堆栈溢出

时间:2013-04-12 16:21:57

标签: c++ function recursion stack-overflow

作为C ++编程和计算机系统架构的初学者,我仍然在学习C ++的基础知识。昨天我读到了递归函数,所以我决定写自己的,这是我写的:(非常基本的)

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

}

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

关于为什么的任何想法?

6 个答案:

答案 0 :(得分:23)

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

让我感到惊讶的是,您的计算机只需要4793次调用即可溢出堆栈。这是一个非常小的堆栈。通过比较,在我的计算机上运行相同的代码需要在程序崩溃之前调用大约100倍。

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

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

int returnZero(int anyNumber) {
  return 0;
}

这需要两个装配说明:

_returnZero:
        xorl    %eax, %eax
        ret

非常整洁。

答案 1 :(得分:2)

您只需点击系统调用堆栈的大小限制,就会发生这种情况。由于某种原因,系统中的堆栈很小,4793函数调用的深度相当小。

答案 2 :(得分:2)

您的筹码数量有限,因此当您拨打4793来电时,4792就会达到限制。每个函数调用都会使用堆栈上的一些空间来保存住宅,也可能是参数。

此页面给出了在递归函数调用期间堆栈的外观的example

答案 3 :(得分:1)

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

答案 4 :(得分:1)

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

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

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

答案 5 :(得分:0)

使用递归,您可以实现SuperDigit:

id  name    Size   Num.smaller.in.B
==============================
1   Apple   7      2
2   Orange  15     5
3   Banana  22     6
4   Kiwi    2      1
5   Melon   28     7
6   Peach   9      3