对于递归函数,大家都很熟悉了,对于解析一些复杂数据结构方面,能够使代码非常简洁,明了。
从我的理解来说,递归函数一般效率比较低,而且自身特点导致的限制也不少:
1. 效率低
一个非常简单的例子数数吧,从1数到1M。
对于循环实现的:
递归实现:
对于循环实现,函数相当于:
对于递归实现,相当于:
对于第一种实现,不需要函数调用,只需要重复1M-1次跳转,即Loop
对于递归实现,需要1M-1次函数调用
让我们来看看跳转指令和函数调用指令的区别:
对于跳转指令,相当于将目标指令地址附给eip寄存器,该条指令结束后计算机会从eip指向的指令地址开始执行。
即:
jmp xxxx ;此指令的工作是仅仅是修改 eip的值为xxxx对应的地址, 当该指令结束后系统会从eip指向处开始执行
对于函数调用指令call,则作了更多隐藏工作:
call xxxx ;因为call指令是需要返回的,所以需要保存返回后的地址,该地址为执行call指令时的eip值+5(call指令的长度为5,32位系统)
;然后将这个值保存到堆栈上,之后才能将xxxx对应的值装载到eip寄存器之中,开始实际跳转动作
;自过程结束后,再将保存在堆栈上的值重新附给eip,这样程序才能从call xxxx的下一条指令开始执行。
所以call xxxx 基本相当于
push eip+5
mov eip, xxxx
jmp eip
ret 相当于:
pop eip ;即将前面的eip+5重新附给eip
jmp eip
从这里基本可以看出:递归函数实际上多做了(1M-1)次多余的工作,而且这只是最简单的寒暑,如果自过程中还有很多临时变量,则整个过程中
维护堆栈平衡的开销也将非常可观。
2. 局限性
不知道这个算不算,因为世纪中还没真正遇到过这个问题导致的运行错误。
对于一个最简单的递归函数:
这个函数执行的结果是导致运行栈耗尽,最终程序崩溃,当然最终原因是它缺少适当的递归出口。
不过对于一种极端情况:
这个函数每次都会在栈上申请 10000 或20000个BYTE, 取决于字符集属性,由于运行栈大小是预先定义好的(2M左右吧),所以该函数也不可避免的导致上面的问题。
综上,对于对效率要求很高的代码应尽量避免使用递归;
在使用递归时,如果递归深度不能预期, 避免在堆栈上申请只有函数返回才能释放的大块空间,而应该将其放在heap上。
以上为个人理解,估计肯定有先人总结过了,但绝非抄袭,同时欢迎拍砖。