尾递归分析
来源:互联网 发布:毛笔书法 知乎 编辑:程序博客网 时间:2024/05/21 22:46
昨天被问到了尾递归及编译器对它的处理相关,一直对它没有研究过,解释得很含糊。
回来查了下,记录如下:递归有线性递归(普通的递归)和尾递归。
由于尾递归的特殊性,一般的编译器会做些特殊处理。因此,在效率和开销上,比普通递归好。
举个例子,计算n!
1)线性递归:
type recurve(long n)
{
return (n == 1) ? 1 : n * recurve(n - 1);
}
2)尾递归:
type recurve_tail(long n, long result)
{
return (n == 1) ? result : recurve_tail(n - 1, result * n);
}
再封装成1)的形式:
type recurve(long n)
{
return (n == 0) ? 1 : recurve_tail(n, 1);
}
分析:
很容易看出, 普通的线性递归比尾递归更加消耗资源。每次调用都使得调用链条不断加长,系统不得不开辟新的栈进行数据保存和恢复;而尾递归就
不存在这样的问题, 因为他的状态完全由n 和 a 保存,并且,被调用函数返回的值即为要求的值,本函数再没有作用,于是本函数不再保存,直接在本函数堆栈上进行递归调用,
对于特殊情况,甚至可以不使用内存空间,直接在寄存器完成。
编译器如何判断是否尾递归?
返回的值是函数本身,没有其它选择。
看一下上述尾递归函数在gcc 4.3.2-1-1下未进行优化的编译结果:
.file "rec.c" .text .globl recurve_tail .type recurve_tail, @function recurve_tail: pushl %ebp movl %esp, %ebp subl $24, %esp cmpl $1, 8(%ebp) je .L2 movl 12(%ebp), %eax movl %eax, %edx imull 8(%ebp), %edx movl 8(%ebp), %eax subl $1, %eax movl %edx, 4(%esp) movl %eax, (%esp) call recurve_tail movl %eax, -4(%ebp) jmp .L3 .L2: movl 12(%ebp), %eax movl %eax, -4(%ebp) .L3: movl -4(%ebp), %eax leave ret .size recurve_tail, .-recurve_tail .ident "GCC: (Debian 4.3.2-1.1) 4.3.2" .section .note.GNU-stack,"",@progbits未进行优化,与普通递归处理方式相同,新开辟了栈;再看-O3优化结果: .file "rec.c" .text .p2align 4,,15 .globl recurve_tail .type recurve_tail, @function recurve_tail: pushl %ebp movl %esp, %ebp movl 8(%ebp), %edx movl 12(%ebp), %eax cmpl $1, %edx je .L2 .p2align 4,,7 .p2align 3 .L5: imull %edx, %eax subl $1, %edx cmpl $1, %edx jne .L5 .L2: popl %ebp ret .size recurve_tail, .-recurve_tail .ident "GCC: (Debian 4.3.2-1.1) 4.3.2" .section .note.GNU-stack,"",@progbits
*在makfile文件开头定义CFLAGS = -O2,这样使用makefile编译时才会优化;gdb中查看函数地址命令为:info line【函数名】;查看某个函数的汇编指令为:disass 【函数名】
原文出处:http://blog.csdn.net/hilyoo/article/details/4445476
- 尾递归分析
- 递归分析
- 递归下降分析程序
- 数据结构:递归 算法分析
- 递归下降分析法
- 递归程序分析
- 递归 塔罗牌分析
- 递归算法详细分析
- Java递归分析
- java递归分析
- 递归算法分析-分享
- 递归下降分析子程序
- 递归算法详细分析
- TreeView的递归分析
- 递归算法详细分析
- 递归算法分析
- 分治与递归分析
- 递归与分治分析
- 字符转换为十六进制
- 为Android仿真器创建并使用SD
- web.xml 中的listener、 filter、servlet 加载顺序及其详解
- ECLIPSE自动生成注释
- 【系统库】常用软件下载
- 尾递归分析
- c++实现广度优先和深度优先查找目录结构
- Struts2的类型转换的一个例子
- thinkphp混编写法页面生成静态化
- 算法学习之路
- Linux中变量$#,$@,$0,$1,$2,$*,$$,$?的含义
- Oracle左连接,右连接
- SlickGrid 插件开发(2):单元格合并功能实现
- 给想从事数据挖掘工作的人介绍的dataguru课程