1002_尾递归为何没有层层递增的调用栈

尾递归

尾递归可以直接销毁调用栈(也有可能是每次递归都是用同一个栈)、而不使栈空间层层叠加。


先看尾递归代码

// 打印链表的每一个元素
1  void c_List_print_data(p_Node_t pList)
2  {
3      if(pList == NULL)
4      {
5          return;
6      }
7  	   printf("%d, ", pList->data);
8  	   c_List_print_data(pList->Next);
9 
10 }
11 
12 void main(void)
13 {
14     int x;
15     c_List_print_data(phead);
16     x = 1;
17 }

递归发生在第 8 条语句 c_List_print_data(pList->Next);,即 c_List_print_data() 函数作为调用者,去调用另一个函数(调用和自己代码一模一样的函数 c_List_print_data())、形成递归。
特别地、第 8 条语句是调用者的最后一条语句:发生递归调用时、调用者本身已经没有其他语句需要执行了,那么其中的局部变量都不会再被使用到了,这个调用栈就可以被销毁释放掉。
所以、我们可以在递归调用发生时、将调用者的调用栈销毁回收,同时给被调用者创建一个新的调用栈(也可以在刚销毁的位置创建、或者不销毁直接初始化后就使用)。

同时、在被调用者返回后、调用者将运行到第 9 行:调用者自己也将返回。
调用者和被调用者最终都返回到同一个地方(第 15 条语句),也就是说它们的返回地址可以设置成一样,或者说可以不修改返回地址,也不新增新的一样的返回地址去多次返回,只需要在最后一次被调用者返回后(执行到退出条件)、做一次返回即可。
具体如何实现、就看不同编译器如何实现了。


图示说明:

假设链表有2个元素:

第一次进入 c_List_print_data() 时、调用栈如下:

在执行前面 7 条语句的过程中,这个调用栈都不变。


执行第 8 条语句时、第二次调用 c_List_print_data()。
假如不销毁当前调用栈,并创建新的调用栈,那么调用栈情况将会是下面这样:

但是现在、我们在第二次调用 c_List_print_data() 前、就先销毁调用栈,再调用 c_List_print_data(),并为其创建新的调用栈,那么调用栈情况如下:

第一次调用 c_List_print_data() 时的局部变量(50)已被销毁,取而代之的是现在的(90)。


最后一次递归时的调用栈如下:

这次调用返回后、整个递归就执行完毕并返回,程序将执行第 16 条语句。

posted @ 2023-06-28 20:31  明天再取个名字  阅读(25)  评论(0)    收藏  举报