递归占用的栈内存有多大

lion7beck 2013-04-10 07:49:27
C语言中,递归时使用栈内存。由于栈维护了每个函数调用的信息直到函数返回后才释放,这需要占用相当大的空间,尤其是程序中使用了许多递归调用的情况下。除此之外,因为有大量信息需要保存和恢复,因此生成和销毁栈帧需要耗费一定的时间。
“相当大的空间”,大约在什么数量级?我一直没有具体的概念。以下C代码中,大家可以帮我评估下吗?非常感谢!
假定分别执行fact(1000)和fact(10000)。


/* fact.c */
#include "fact.h"
int fact(int n) {
if(n < 0)
return 0
else if (n == 0)
return 1;
else if (n == 1)
return 1;
else
return n*fact(n-1);
}

...全文
1309 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
lion7beck 2013-04-19
  • 打赏
  • 举报
回复
感谢各位!!
fuchen 2013-04-16
  • 打赏
  • 举报
回复
尾递归编译器容易优化成循环。
引用 11 楼 lion7beckham 的回复:
引用 9 楼 icessl 的回复:Q2. 尾递归可以大大提高效率 A2. 提高的幅度很有限,可以忽略. 若用尾递归的话,开辟一个栈帧就可以了!不会像普通递归那样,递归多少次就开辟多少栈帧。所以提高的幅度肯定很大啊!
youyou1912 2013-04-15
  • 打赏
  • 举报
回复 1
debug版本和release版本不一样, CPU不同也不一样. 提供几个简单的思路. 1. 在最外层和最深层也就是递归退出的地方, 打印出当前栈所处地址. 两者相减大概可以知道栈空间使用. 2. a. 修改link工具, 让exe默认的stack比较小. b. 创建一个线程专门做递归调用, 创建线程的时候, 给定一个reserve/commit的栈大小. 允许. 成功了, 就重复, 每次都缩小stack, 直到遇到一个stack大小. 线程栈破坏退出. 这样就可以知道精确的堆栈的需求. (当然包含了系统线程启动函数和异常帧的大小)
tkminigame 2013-04-12
  • 打赏
  • 举报
回复
可以做一个无限递归,把磁盘和内存都占满的程序。
lion7beck 2013-04-12
  • 打赏
  • 举报
回复
引用 9 楼 icessl 的回复:
Q1. 若返回值是个大结构体,那必然不是4字节了吧. A1. 是的. 所以不主张返回结构体,而是返回指针. Q2. 尾递归可以大大提高效率 A2. 提高的幅度很有限,可以忽略. Q3. 是否还需要考虑栈帧之间的空余内存 A3. 不需要.实际上,栈帧之间没有空余内存.
睡得够晚,哈哈!!
lion7beck 2013-04-12
  • 打赏
  • 举报
回复
引用 9 楼 icessl 的回复:
Q2. 尾递归可以大大提高效率 A2. 提高的幅度很有限,可以忽略.
若用尾递归的话,开辟一个栈帧就可以了!不会像普通递归那样,递归多少次就开辟多少栈帧。所以提高的幅度肯定很大啊!
lion7beck 2013-04-12
  • 打赏
  • 举报
回复
引用 3 楼 chouy 的回复:
好像操作系统不同,对栈的大小定义也不同。 在JAVA中定义栈大小默认是48K,最小可以设置为32K。
这么小吗?10000次的递归都完成不了?!
icessl 2013-04-11
  • 打赏
  • 举报
回复
Q1. 若返回值是个大结构体,那必然不是4字节了吧. A1. 是的. 所以不主张返回结构体,而是返回指针. Q2. 尾递归可以大大提高效率 A2. 提高的幅度很有限,可以忽略. Q3. 是否还需要考虑栈帧之间的空余内存 A3. 不需要.实际上,栈帧之间没有空余内存.
lion7beck 2013-04-11
  • 打赏
  • 举报
回复
引用 6 楼 icessl 的回复:
一般而言,在 X86 32位系统中,一次函数调用消耗的栈空间大约是: 4字节返回地址; 4*n字节的参数,n是参数个数; 12字节的寄存器保护区(EBP ESI EDI) 4*m字节局部变量,m 是声明在函数内部变量的个数 所以,对于楼主给出的实例,一次调用耗用的内存大小是 4+4*1+12=20字节 若递归 1000 次,大约耗 20KB 栈空间 若递归……
是否还需要考虑栈帧之间的空余内存?
lion7beck 2013-04-11
  • 打赏
  • 举报
回复
引用 6 楼 icessl 的回复:
一般而言,在 X86 32位系统中,一次函数调用消耗的栈空间大约是: 4字节返回地址; 若递归……
太感谢啦,学习了! 不过钻个牛角尖:若返回值是个大结构体(当然一般都会用指针返回。。),那必然不是4字节了吧? 此外,自己再查了下,发现尾递归可以大大提高效率。对开头那个例子,可以这样实现: 1.若n=1或n=0,F(n,a) = a; 2.若n>1,F(n,a) = F(n-1,na)。
icessl 2013-04-11
  • 打赏
  • 举报
回复
一般而言,在 X86 32位系统中,一次函数调用消耗的栈空间大约是: 4字节返回地址; 4*n字节的参数,n是参数个数; 12字节的寄存器保护区(EBP ESI EDI) 4*m字节局部变量,m 是声明在函数内部变量的个数 所以,对于楼主给出的实例,一次调用耗用的内存大小是 4+4*1+12=20字节 若递归 1000 次,大约耗 20KB 栈空间 若递归 10000 次,大约耗 200KB 栈空间
lion7beck 2013-04-11
  • 打赏
  • 举报
回复
请教大家我这么理解思路对吗?
引用 2 楼 lion7beckham 的回复:
谢谢!不过我这里主要想知道对栈内存的使用情况。请教一下:是否主要是形参n和返回值的占用总空间再乘以1000或者10000呢? ……
商科程序员 2013-04-10
  • 打赏
  • 举报
回复
好像操作系统不同,对栈的大小定义也不同。 在JAVA中定义栈大小默认是48K,最小可以设置为32K。
lion7beck 2013-04-10
  • 打赏
  • 举报
回复
谢谢!不过我这里主要想知道对栈内存的使用情况。请教一下:是否主要是形参n和返回值的占用总空间再乘以1000或者10000呢?
引用 1 楼 FancyMouse 的回复:
对于x86架构来说,基本就是push和pop指令。函数调用的时候一般只会push函数自己会用到的寄存器。用到很多寄存器的话那就会push很多东西。 具体push了什么东西直接翻汇编就知道了。
FancyMouse 2013-04-10
  • 打赏
  • 举报
回复
对于x86架构来说,基本就是push和pop指令。函数调用的时候一般只会push函数自己会用到的寄存器。用到很多寄存器的话那就会push很多东西。 具体push了什么东西直接翻汇编就知道了。
qq120848369 2013-04-10
  • 打赏
  • 举报
回复
深度不可预估还是别递归了.

33,008

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧