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

What is the order of magnitude of the maximum number of recursive calls in C++?

本文关键字:数量级 多少 调用 递归 C++      更新时间:2023-10-16

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

所以,正如你所猜到的,问题是(同名的)堆栈溢出。 每次调用都需要设置一个新的堆栈帧,将新信息推送到堆栈上;堆栈大小是固定的,最终会用完。

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

很难

弄清楚在实践中相当于多少次调用。 函数的堆栈大小由它的局部变量、返回地址的大小以及参数的传递方式(有些可能会在堆栈上)决定,其中大部分也取决于架构。 通常,您可能会假设后两个小于一百字节(这是一个粗略的估计)。 前者取决于您在函数中执行的操作。 如果你假设函数在堆栈上占用 256 字节,那么对于 1M 的堆栈,你会在溢出之前得到 4096 个函数调用——但这还没有考虑到主函数的开销等。

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

这完全取决于您在堆栈上使用多少信息。但是,Windows上的默认堆栈是1MB,Unix上的默认堆栈是8MB。简单地进行调用可能涉及推送一些32位寄存器和返回地址,例如,因此您可以查看每个调用的20字节,这将使Windows上的最大值约为50k,Unix上的最大值为400k - 对于空函数。

当然,据

我所知,您可以更改堆栈大小。

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

有一些工具可以测量堆栈使用情况。他们预先用特定的字节模式填充堆栈,然后查看它被更改的地址。有了这些,您可以了解您离极限有多近。

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

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

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

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

归的最大深度没有固定的限制;如果总使用量超过系统施加的堆栈限制,则会出现堆栈溢出。

您可以通过以某种方式减少递归

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

正如其他人所说,您也许可以增加允许的堆栈大小,但这可能只有有限的用途 - 这是您在运行程序之前必须做的额外事情。 它还可能消耗资源并干扰系统上的其他进程(施加限制是有原因的)。

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