理解递归的诀窍是理解堆栈。
我在一个名为 的函数的第 2 行main
,我所有的局部变量都存储在我的堆栈帧中:
+------------------+
| main() variables | The stack frame
+------------------+
然后我调用fib(3)
,因此计算机将我的当前位置(EIP)推送到堆栈,然后为 fib 创建一个新的堆栈帧并将其添加到上面。我只能访问顶部堆栈框架:
+------------------+
| fib() n = 5 | Top of stack (current frame)
+------------------+
| EIP: main @ 2,1 |
+------------------+
| main() variables | Bottom of stack
+------------------+
在第 4 行fib
,它再次调用fib
,所以同样的情况再次发生:
+------------------+
| fib() n = 4 | Top of stack
+------------------+
| EIP: fib @ 4,1 |
+------------------+
| fib() n = 5 |
+------------------+
| EIP: main @ 2,1 |
+------------------+
| main() variables | Bottom of stack
+------------------+
当函数被递归调用时,它会一次又一次地这样做。堆栈不断增长,直到有东西返回,此时,在第 2 行fib
,它返回 1。这会弹出顶部堆栈帧并将其丢弃,然后将执行返回到保存的执行指针,程序从中断处继续
+------------------+
| fib() n = 3 | Top of stack
+------------------+
... etc ...
+------------------+
| EIP: main @ 2,1 |
+------------------+
| main() variables | Bottom of stack
+------------------+
最终你会回到调用函数中(或者你会因为它变得太大而导致堆栈溢出)。要记住的关键是,每次调用该函数时,它都会获得一个包含所有局部变量的新堆栈帧,并且您之前的位置会被保存。那是递归。
主要问题是在教人们递归时,每个人总是使用斐波那契数列,这意味着在一行上有两个递归函数调用。这是不必要的混乱,我相信你会同意的!