《C Primer Plus 》例题解析 程序清单 9.6 recur.c 程序
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void up_and_down(int);
int main(void)
{
up_and_down(1);
return 0;
}
void up_and_down(int n)
{
printf("Level %d: location %p\n", n, &n); // #1
if (n < 4)
up_and_down(n + 1);
printf("LEVEL %d: location %p\n", n, &n); // #2
}
运行结果:
Level 1: location 004FFE18
Level 2: location 004FFD40
Level 3: location 004FFC68
Level 4: location 004FFB90
LEVEL 4: location 004FFB90
LEVEL 3: location 004FFC68
LEVEL 2: location 004FFD40
LEVEL 1: location 004FFE18
解析
&n是指n的地址
printf1 打印出来的顺序是1234,是因为它在 if 语句之前,每一级函数开始的时候,还没遇到递归判断条件就先打印了它。
printf2 打印出来的顺序是4321,是因为它在 if 语句之前,当函数开始的时候,碰到 if 语句,进入递归,if 语句里面进行一次又一次地函数调用,只有达到了递归的限制条件(n=4)才算调用完了,这时候才会打印 if 语句后面 printf2,而且printf2 是从里层函数往外打印的,所以是4321。
递归就像洋葱,假设一个洋葱横切,从外面一层一层打开,每一层都调用一次函数,而每一层函数up_and_down()都有一个printf1 和一个printf2。
每剥开一片洋葱,if 语句之前的 printf1 先打印一次,等剥到洋葱最里面,不能继续往里剥的时候,也就是不再执行 if 语句的时候,那 if 语句后面的 printf2 就开始打印了。
打印完最里面的printf2,再打上一层函数带的 printf2,然后再打上上一层函数带的 printf2,也就是printf2 是从最里面往外面一层一层打印的。
每一层函数他们的 n 的值是不一样的,但是同一级函数里面,printf1 和printf2 里面 n 的值是一样的,虽然打印的时间有先后,导致printf1 打印的是1234,printf2 打印的是4321,但是,printf1 里面的1和printf2 里面的1 地址是一样,2和2地址是一样的,3和3地址是一样的,4和4地址也是一样的。
千言万语都在下面图表中了:
注意,这样分解仅仅是便于理解递归的调用过程,递归的基本原理有6个:
- 每级函数调用都有自己的变量;
- 每级函数调用都会返回一次,当函数执行完毕后,控制权将被传回上一级递归;
- 递归函数中位于递归调用之前的语句,均按被调函数的顺序执行;
- 递归函数中位于递归调用之后的语句,均按被调函数相反的顺序执行;
- 虽然每级递归都有自己的变量,但是并没有拷贝函数的代码。(下面的分解程序是博主自己写出来便于理解的。)程序按顺序执行函数中的代码,而递归调用就相当于又从头开始执行函数的代码。只是每次为递归创建一个变量,递归调用非常类似于一个循环语句。
- 递归函数必须包含能让递归调用停止的语句。通常,递归函数都使用 if 或其他等价的测试条件再函数形参等于某特定值时终止递归。
将下面程序于递归的基本原理对应起来如下:
/*5.虽然每级递归都有自己的变量,但是并没有拷贝函数的代码。下面的分解程序是博主自己写出来
便于理解的。程序按顺序执行函数中的代码,而递归调用就相当于又从头开始执行函数的代码。只是
每次为递归创建一个变量,递归调用非常类似于一个循环语句。*/
void up_and_down(int n) //n=1
//1.每级函数调用都有自己的变量
//2.每级函数调用都会返回一次,当函数执行完毕后,控制权将被传回上一级递归
{
printf("Level %d: location %p\n", n, &n); // #1
//3.递归函数中位于递归调用之前的语句,均按被调函数的顺序执行。
if (n < 4)
{
up_and_down(1 + 1); //n=2
//1.每级函数调用都有自己的变量
//2.每级函数调用都会返回一次,当函数执行完毕后,控制权将被传回上一级递归
{
printf("Level %d: location %p\n", n, &n); // #2
//3.递归函数中位于递归调用之前的语句,均按被调函数的顺序执行。
if (n < 4)
{
up_and_down(2 + 1); //n=3
//1.每级函数调用都有自己的变量
//2.每级函数调用都会返回一次,当函数执行完毕后,控制权将被传回上一级递归
{
printf("Level %d: location %p\n", n, &n); // #3
//3.递归函数中位于递归调用之前的语句,均按被调函数的顺序执行。
if (n < 4)
{
up_and_down(3+ 1); //n=4
//1.每级函数调用都有自己的变量
//2.每级函数调用都会返回一次,当函数执行完毕后,控制权将被传回上一级递归
{
printf("Level %d: location %p\n", n, &n); // #4
//3.递归函数中位于递归调用之前的语句,均按被调函数的顺序执行。
if (n < 4) //条件不符合
/*6.递归函数必须包含能让递归调用停止的语句。通常,递归函数都
使用if或其他等价的测试条件再函数形参等于某特定值时终止递归。*/
{
up_and_down(4 + 1);
}
printf("LEVEL %d: location %p\n", n, &n); // #4
//4.递归函数中位于递归调用之后的语句,均按被调函数相反的顺序执行。
}
}
printf("LEVEL %d: location %p\n", n, &n); // #3
//4.递归函数中位于递归调用之后的语句,均按被调函数相反的顺序执行。
}
}
printf("LEVEL %d: location %p\n", n, &n); // #2
//4.递归函数中位于递归调用之后的语句,均按被调函数相反的顺序执行。
}
}
printf("LEVEL %d: location %p\n", n, &n); // #1
//4.递归函数中位于递归调用之后的语句,均按被调函数相反的顺序执行。
}