问题描述
我知道对于C来说,每次调用递归函数时,至少将堆栈帧和返回地址写入堆栈,但是有一种晦涩的方法来使它不耗尽内存吗?显然,这纯粹是一个假设问题,因为我无法想象它的用例。
解决方法
您可以使用堆栈来模拟递归
与函数调用和静态变量相关的内存部分(在C中用int x;
声明)与与动态分配相关的内存部分(在C中使用malloc()
)是分开的。只有前者(称为“堆栈”)受到限制,并且会导致“堆栈溢出”错误。好吧,当然这并不完全正确。后者被称为“堆”,当然,您的计算机并不是万能的,如果您真正尝试突破其极限,则它会在某个时刻耗尽内存。
- Recursive function to loop and stack
- How can you emulate recursion with loop and stack?
- How ti rewrite a recursive method by using a stack?
- Way to go from recursion to iteration
您可以使用尾部递归来避免在调用堆栈中添加层
堆栈溢出是由于调用堆栈的大小引起的。想象一下这样的函数:
int f(int n)
{
int x;
if (n < 2)
{
return 1;
}
else
{
x = f(n-1);
return n * x;
}
}
在对f
进行递归调用时,计算机需要注意以下事实:一旦完成递归调用,我们将需要再进行一次乘法运算。注意这一点是通过在“调用堆栈”中添加一层有关变量值以及代码在哪里的信息来实现的。这需要内存,以防在堆栈太大的情况下导致堆栈溢出。
现在与以下代码进行比较:
int f(int n,int acc)
{
if (n < 2)
{
return acc;
}
else
{
return f(n-1,n * acc);
}
}
这次,将递归调用直接封装在return
中,这意味着在递归调用之后无需进行其他工作。想象一下,您要我工作并向您报告结果;通过进行递归调用,我将一些工作委托给我的朋友;然后,我不再呆在身边等我的朋友向我报告,以便我可以向您报告,而是立即离开并告诉我的朋友直接向您报告。这样可以通过“剪切中间人”来节省内存。
了解更多:
在具有惰性求值的语言中,您可以编写一个看似无限递归的函数,然后仅根据需要求值: