作为C ++编程和计算机系统架构的初学者,我仍然在学习C ++的基础知识。昨天我读到了递归函数,所以我决定写自己的,这是我写的:(非常基本的)
int returnZero(int anyNumber) {
if(anyNumber == 0)
return 0;
else {
anyNumber--;
return returnZero(anyNumber);
}
}
当我这样做时:int zero1 = returnZero(4793);它会导致堆栈溢出,但是,如果我将值4792作为参数传递,则不会发生溢出。
关于为什么的任何想法?
答案 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