c ++中的递归函数行为

时间:2010-12-02 06:27:09

标签: c++ recursion

我知道递归函数的基本定义.....但我想知道它对内存的影响? 为什么不选择循环或迭代?

7 个答案:

答案 0 :(得分:6)

通常不会出于以下两个原因之一选择它:

  • 编码器不理解递归(特别是尾端优化);和
  • 堆叠空间可能很贵。

虽然标准对堆栈空间没什么好说的,但它往往是一个容易出现溢出的有限资源。

例如,这是一个糟糕的递归函数:

def addTwoPositiveNumbers (a,b):
    if b == 0:
        return a
    return addTwoPositiveNumbers (a+1,b-1)

(除非它可以做尾部优化)。那是因为它理论上可以使用堆栈级别的 lot ,例如当b = 1,000,000,000时,它将使用十亿个堆栈帧。

另一方面,二进制搜索也不算太糟糕,因为它使用每个级别的递归删除了一半的搜索空间。因此,即使您有十亿个条目,也只能记录 2 1,000,000,000或30个堆栈级别。

列出了我认为人们避免它的原因,我应该说我使用它很多,你应该意识到许多问题自然表达为一种递归解决方案。代码更清晰,更易读。仅仅因为这个原因,它应该是你的武器库的一部分。

答案 1 :(得分:2)

C ++不要求尾部调用优化,因此可以简单地转换为循环的递归函数仍然可以在调用深度(存储帧)中占用线性空间。

此外,C / C ++不会必然检测到堆栈溢出,因此这可能是深度调用的另一个问题。

已编辑以获得更多合格语言(“必然”)

修改

有些人似乎对gcc和clang等特定编译器执行尾调用优化和/或堆栈溢出检测这一事实感到困惑。关键是,如果没有强制要求,那就不安全了。例如,gcc仅在-O2,-O3和-Os执行尾调用优化。因此,如果程序依赖执行尾调用优化,当有人编译时没有优化标志(在gcc上),程序将会神秘地死掉。

如果它是良好的编程习惯,那么,无论如何,它们都可以继续编写依赖于编译器标志的程序。或者那些依赖编译器的人。优化不是可选的。

答案 2 :(得分:2)

您始终可以使用迭代操作重写递归 - 问题变为可读性/易于实现。对于相当多的问题,递归导致更简单的算法(例如,与图/树相关的大多数东西)。

就内存而言:在C或C ++这样的语言中,每个函数调用都需要将其参数推送到调用堆栈。这个堆栈的大小有限(100个长的调用链几乎总是好的 - 1000000几乎肯定不行)。确切的限制是高度依赖系统的。

更多高级语言,特别是功能性语言,有“技巧”来启用堆栈,在某些情况下看似“无限”(尾部调用,这是非常常见的情况)。这些语言通常保证优化,称为尾部调用优化(TCO),它可以在递归有意义的情况下实现优雅的代码。

答案 3 :(得分:0)

除非编译器能够优化递归调用(尾递归优化),否则每个invokation都需要所有参数和局部变量的复制以及返回地址。那东西必须存放在通常在堆栈上的某个地方。因此影响是一次调用的内存量乘以后续调用的次数。

此外,当invokation深入到递归时,堆栈的新形式被占用,这可能导致缓存未命中,这可能会降低执行速度,也可能归因于内存影响。

答案 4 :(得分:0)

考虑递归实际上是如何工作的,并且对内存的影响是明确的。首先要了解一般的函数调用是如何工作的。您可能想要启动here

答案 5 :(得分:0)

通常可以避免使用递归函数,因为

  • 可能需要太多的灰色细胞
  • 堆叠空间可能比理想/允许的要多。

如果函数不可重入,它们也可能会产生不良后果。

答案 6 :(得分:0)

由于堆栈溢出问题,通常会避免递归。迭代解决方案没有这个问题,所有递归问题都可以重写为使用迭代(有些比其他更容易)。此外,许多程序员根本不熟悉递归(它经常不会出现这种情况) - 稍后维护代码的人可能无法理解正在发生的事情并进行修改,从而搞砸了一些东西。这通常是一件坏事。其他语言(特别是功能语言)的递归通常更常见,因为它们处理它的方式。