C ++中最大递归调用次数的数量级是多少?

时间:2012-04-13 18:05:35

标签: c++ stack-overflow

我有一个递归函数,在给定某些输入的情况下会调用很多次 - 这正是应该做的。我知道我的函数不是无限循环 - 它只是达到一定数量的调用和溢出。我想知道在堆栈上放置太多内存是否存在问题,或者只是调用数量的正常限制。显然很难说出一个特定数量的呼叫是最大的,但任何人都能给我一个粗略估计的数量级吗?它是成千上万?数百?百万?

5 个答案:

答案 0 :(得分:3)

因此,正如您所猜测的那样,问题是(同名)堆栈溢出。每次调用都需要设置一个新的堆栈帧,将新信息压入堆栈;堆栈大小是固定的,并最终耗尽。

什么设置堆栈大小?这是编译器的属性 - 也就是说,它是针对二进制可执行文件修复的。在Microsoft的编译器(在VS2010中使用)中,它默认为1兆字节,您可以在编译器选项中用“/ F”覆盖它(有关'03示例,请参阅here,但语法相同)。

很难弄清楚在实践中有多少电话相等。函数的堆栈大小由它的局部变量,返回地址的大小以及参数的传递方式(有些可能在堆栈中)决定,其中大部分也取决于体系结构。通常,您可以假设后两者小于一百字节(这是一个粗略估计)。前者取决于你在函数中做了什么。如果假设该函数在堆栈上占用了256个字节,那么在使用1M堆栈时,您将在溢出之前获得4096个函数调用 - 但这并未考虑主函数的开销等。

您可以尝试减少局部变量和参数开销,但真正的解决方案是Tail Call Optimization,其中编译器在调用递归函数时释放调用函数。您可以在MSVC here中阅读更多相关信息。如果您不能进行尾调用,并且无法以可接受的方式减小堆栈大小,那么您可以使用“/ F”参数查看增加堆栈大小,或者(首选解决方案)查看重新设计。

答案 1 :(得分:2)

这完全取决于您在堆栈上使用了多少信息。但是,Windows上的默认堆栈为1MB,Unix上的默认堆栈为8MB。简单地拨打一个电话可能需要推送一些32位寄存器和一个返回地址,这样你可能会看到一个大约20字节的调用,这将使Windows上的最大值为50k,而Unix上的最大值为400k,这是一个空函数。 / p>

当然,据我所知,你可以改变堆栈大小。

答案 2 :(得分:0)

您可以选择更改/增加默认堆栈大小。这是一种方式http://msdn.microsoft.com/en-us/library/tdkhxaks(v=vs.80).aspx

答案 3 :(得分:0)

有一些工具可以衡量堆栈的使用情况。它们使用特定的字节模式预先填充堆栈,然后查看它更改的地址。有了这些,你可以找到接近极限的程度。

也许其中一个valgrind工具可以做到这一点。

答案 4 :(得分:0)

递归函数使用的堆栈空间量取决于递归的深度和每次调用使用的内存空间量。

递归的深度是指在任何给定时刻激活的呼叫级别数。例如,二叉树可能有一百万个节点,但如果它很平衡,你可以在不超过20个同时活动的调用的情况下遍历它。如果它没有很好地平衡,最大深度可能会更大。

每次调用使用的内存量取决于递归函数中声明的变量的总大小。

最大递归深度没有固定限制;如果您的总使用量超过系统强加的堆栈限制,您将获得堆栈溢出。

您可以通过某种方式减少递归的深度来减少内存使用量,可能通过重组您正在遍历的任何内容(您没有告诉我们太多),或通过减少任何内容的总大小在递归函数中声明的本地对象(请注意,堆分配的对象不会影响堆栈大小),或两者的某种组合。

正如其他人所说,你可能会增加你允许的堆栈大小,但这可能只是有限的使用 - 而且在运行你的程序之前你需要做的另外一件事。它还可能消耗资源并干扰系统上的其他进程(由于某种原因,会施加限制)。

更改算法以避免递归可能是可能的,但同样,我们没有足够的信息来说明这一点。