递归函数的内存分配
Posted
技术标签:
【中文标题】递归函数的内存分配【英文标题】:Memory Allocation for Recursive Functions 【发布时间】:2014-04-21 05:24:59 【问题描述】:调用递归函数时如何分配内存? 一个函数有它自己分配的内存。当它被调用时,参数(不是引用传递的)和变量获得内存。那么当函数在函数体内再次被调用时,内存是如何分配给第二次调用的变量和参数的呢?
【问题讨论】:
【参考方案1】:递归函数与任何其他函数没有什么不同——自动局部变量被分配为一个单独的块,通过将堆栈指针推进足够远来解释它们的大小总和(加上对齐所需的任何填充)。
每个递归调用都以这种方式推送一个新的堆栈帧,然后在返回时将其弹出。如果递归未能达到基本情况,堆栈将迅速耗尽,从而导致同名的堆栈溢出崩溃。
【讨论】:
【参考方案2】:递归调用函数就像任何其他函数一样。因此,内存的分配方式与调用任何常规函数的方式相同。
【讨论】:
@Caesar:因为它本质上是没有答案的。你是对的,它与任何其他类型的函数相同,但你没有解释它的实际含义。 @DrewHall 它回答了这个问题,如果他需要知道常规函数如何分配内存,那么这完全是另一个问题。 @Caesar:也许吧。但我认为这个问题的本质是缺乏对调用堆栈机制的理解。【参考方案3】:它与调用任何其他函数相同。变量(不是引用传递的)在堆栈上分配。当函数开始返回时(即达到基本情况),堆栈会按照每个嵌套递归调用被推入堆栈的顺序弹出。
这个 youtube 视频很好地解释了递归函数的调用堆栈:https://www.youtube.com/watch?v=k0bb7UYy0pY
【讨论】:
C++ 不保证自动管理的内存将分配在类似堆栈的结构上。 就像我说的,递归函数被视为任何其他函数,它是在堆栈上分配的。如果您查看程序集,将会有PUSH
和 POP
操作。那只能在堆栈上。顺便说一句,“自动管理的内存”是什么意思?
虽然大多数自动管理的内存是使用堆栈处理的,但 C++ 并不保证所有系统都必须使用堆栈来存储内存。
我同意你的观点。但它适用于函数中的变量。我认为这里的讨论是关于函数堆栈的。虽然一个变量可能在寄存器上分配,但是当另一个函数被调用时,该变量必须存储到堆栈中,否则寄存器只是一个临时占位符,下一个函数可能会使用它。因此,如果变量存储在寄存器中,它必须位于函数堆栈帧上,以在函数调用之间保留值。【参考方案4】:
当一个函数(调用func1
)调用另一个函数(调用它func2
)时,执行func2
所需的数据被压入堆栈。这对于递归函数不会改变(当func2
与func1
相同时)。
如果递归函数被递归调用10
次,将有10
堆栈帧,每个对应于该函数的一次调用。
【讨论】:
【参考方案5】:当一个函数被调用时,必要的参数被压入堆栈。当一个函数完成它的任务时,参数被弹出并且程序计数器被恢复。
递归函数也不例外。
【讨论】:
【参考方案6】:自动变量的内存分配在stack。当您调用另一个函数(递归 self 或其他函数)时,堆栈帧到目前为止仍处于其状态,我们在堆栈上分配更多内存(假设是这样)来存放这个新函数的局部变量。当函数结束时,新的堆栈帧被丢弃,我们恢复到之前的(calee)堆栈帧。
您可以使用您选择的任何调试器来更好地理解事物。有不明白的请追问。
【讨论】:
【参考方案7】:递归使用堆栈内存来执行
看下面的例子
void rev(Node* head)
if(head==NULL) return;
head=head->next;
rev(head);
cout<<head->data<<endl;
让我们看看堆栈段中的递归。
设NODE1 -> NODE2->NULL
其中 NODE1 和 NODE2 是结构对象。
你的函数在做什么:
Call to rev(NODE1)
Check if it is NULL
Point to next NODE i.e. NODE2
Call to rev(NODE2)
Check if It is NULL
Point to next NODE i.e. NULL
Call to rev(NULL)
Check if It is NULL
Pointer will be returned With head = NULL
希望对您有所帮助。
【讨论】:
【参考方案8】:您编写的函数(代码行)存储在文本段中,当它被调用时,它会在堆栈内存中分配一个帧,变量(静态除外)的内存也会在给定帧的堆栈中分配。
递归函数没有什么不同,它也是被调用 n 次的普通函数。所以它的内存(帧)也在堆栈上分配了 n 次。
【讨论】:
【参考方案9】:当一个函数被调用时,它的参数与其他函数状态一起保存在堆栈中。
函数调用过程中构造的栈帧包含以下内容:
函数参数。 函数的返回地址。 帧指针。 异常处理程序框架。 本地声明的变量。 缓冲区。 被调用者保存寄存器。【讨论】:
【参考方案10】:函数参数和局部变量在栈上分配。它们形成了所谓的stack frame。当一个函数被递归调用时,每个函数的递归调用都会分配一个栈帧。
例如if void f() 被递归调用了 3 次。
// Assume stack grows upwards
stack frame #3 <== the most recent call
stack frame #2
stack frame #1
【讨论】:
C++ 不保证自动管理的内存将分配在类似堆栈的结构上。 我认为使用递归函数是事实上的。在这种情况下为什么不呢? @Caesar:虽然 C++ 标准确实没有为此使用“堆栈”一词,但“自动存储持续时间”的指定语义和每个主要处理器指令集架构的机制(ISA ) 几乎保证会使用一个堆栈。【参考方案11】:使用后需要释放内存。
这里有一个示例程序来说明这一点。
http://forums.devx.com/showthread.php?145311-Free-memory-or-garbage-in-recursion-program
【讨论】:
以上是关于递归函数的内存分配的主要内容,如果未能解决你的问题,请参考以下文章