5.3.1 递归定义
如果定义一个概念需要用到这个概念本身,我们称它的定义是递归的(Recursive)。
举例:如n的阶乘(Factorial)是这样定义的:n的阶乘等于n乘以n-1的阶乘。
0! = 1
n! = n · (n-1)!int factorial(int n) { if (n == 0) return 1; else { int recurse = factorial(n-1); int result = n * recurse; return result; } }
自己直接或间接调用自己的函数称为递归函数。这里的
factorial
是直接调用自己,有些时候函数A调用函数B,函数B又调用函数A,也就是函数A间接调用自己,这也是递归函数。图中用实线箭头表示调用,用虚线箭头表示返回,右侧的框表示在调用和返回过程中各层函数调用的存储空间变化情况。
分析:
main()
有一个局部变量result
,用一个框表示。调用
factorial(3)
时要分配参数和局部变量的存储空间,于是在main()
的下面又多了一个框表示factorial(3)
的参数和局部变量,其中n
已初始化为3。
factorial(3)
又调用factorial(2)
,又要分配factorial(2)
的参数和局部变量,于是在main()
和factorial(3)
下面又多了一个框。每次调用函数时分配参数和局部变量的存储空间,退出函数时释放它们的存储空间。factorial(3)
和factorial(2)
是两次不同的调用,factorial(3)
的参数n
和factorial(2)
的参数n
各有各的存储单元,虽然我们写代码时只写了一次参数n
,但运行时却是两个不同的参数n
。并且由于调用factorial(2)
时factorial(3)
还没退出,所以两个函数调用的参数n
同时存在,所以在原来的基础上多画一个框。依此类推,请读者对照着图自己分析整个调用过程。读者会发现这个过程和前面我们用数学公式计算3!的过程是一样的,都是先一步步展开然后再一步步收回去。
我们看上图右侧存储空间的变化过程,随着函数调用的层层深入,存储空间的一端逐渐增长,然后随着函数调用的层层返回,存储空间的这一端又逐渐缩短,并且每次访问参数和局部变量时只能访问这一端的存储单元,而不能访问内部的存储单元,比如当factorial(2)
的存储空间位于末端时,只能访问它的参数和局部变量,而不能访问factorial(3)
和main()
的参数和局部变量。具有这种性质的数据结构称为堆栈或栈(Stack),随着函数调用和返回而不断变化的这一端称为栈顶,每个函数调用的参数和局部变量的存储空间(上图的每个小方框)称为一个栈帧(Stack Frame)。操作系统为程序的运行预留了一块栈空间,函数调用时就在这个栈空间里分配栈帧,函数返回时就释放栈帧。