1递归的定义:
递归:
参加“递归”。
什么?这个定义什么也没有说啊!好吧,说一下:
递归:
如果还是没明白递归是什么意思,参加“递归”。
噢,也许这次你明白了,原来递归就是“自己用到自己”的意思。
也可理解为:
A经理:“这事不归我管,去找B经理。”于是你去找B经理。
B经理:“这事不归我管,去找A经理。”于是你又回到了A经理这儿。
接下来发生的事情就不难想到了。是要两个经理的说辞不变,你又始终听话,你将会永远往返于两个经理之间,这叫无限递归(Infinite Recursion)。尽管在这里,A经理并没有让你找他自己,但还是回到了他这里。换句话说,“间接地用到自己”也算递归。
2栈:
2.1调用栈(递归中用到的一个知识,粗略讲解):
all stack(通常译作“调用栈”)也是计算机系统中的一个重要概念。在介绍 call stack 之前,我们首先来回顾一下 procedure 是什么。
在计算机程序当中,一个 procedure(通常译作“过程”)吃进来一些参数,干一些事情,再吐出去一个返回值(或者什么也不吐)。我们熟悉的 function、method、handler 等等其实都是 procedure。
当一个 procedure A 调用另一个 procedure B 的时候,计算机其实需要干好几件事。
一是转移控制——计算机要暂停 A 并开始执行 B,并让 B 在执行完之后还能回到 A 继续执行。
二是转移数据——A 要能够传递参数给 B,并且 B 也能返回值给 A。
三是分配和释放内存——在 B 开始执行时为它的局部变量分配内存,并在 B 返回时释放这部分内存。
同学们想一下,假设 A 调用 B,B 再调用 C,C 执行完返回给 B,B 再执行完返回给 A,哪种数据结构最适合管理它们所使用的内存?没错,是 stack,因为过程调用具有 last-in first-out 的特点。当 A 调用 B 的时候,A 只要将它需要传递给 B 的参数 push 进这个 stack,再把将来 B 返回之后 A 应当继续执行的指令的地址(学名叫 return address)也 push 进这个 stack,就万事大吉了。之后 B 可以继续在这个 stack 上面保存一些寄存器的值,分配局部变量,进而继续构造调用 C 时需要传递的参数等等。
这个 stack 其实就是我们所说的 call stack。(这里的描述有些简化,实际当中计算机会做一些优化,如果参数和局部变量不太多的话就懒得放在 call stack 里,而是直接使用寄存器了。)
Call stack 在 virtual memory 里其实就是一段连续的地址空间,靠一个叫做 SP 的寄存器(32-bit 叫 ESP,64-bit 叫 RSP)来指向栈顶。既然是连续的,于是它在使用上比我们理论课上讲的抽象的 stack 要更灵活一些,更接近 array 而不是 linked list,可以访问任意元素,而不仅仅是栈顶元素。(当然进栈出栈还是只能在栈顶进行。)这也就是为什么尽管它叫做 call stack,我们依然可以同时有不止一个参数和不止一个局部变量的原因。
如果任然无法理解,可以做如下比喻。
皇帝(拥有main函数的栈帧):大臣你给我算一下f(3)。
大臣(拥有f(3)函数的栈帧):大臣你给我算一下f(2)。
知府(拥有f(2)函数的栈帧):大臣你给我算一下f(1)。
县令(拥有f(1)函数的栈帧):大臣你给我算一下f(0)。
师爷(拥有f(0)函数的栈帧):回老爷,f(0)=1。
县令(心算f(1)=f(0)*1=1):回知府,f(1)=1。
知府(心算f(2)=f(1)*2=2):回大人,f(2)=2。
大臣(心算f(3)=f(2)*3=6):回皇上,f(3)=6。
2.2函数栈帧的创建和销毁:
2.2.1寄存器:
eax
ebx
ecx
edx
ebp
esp等
且ebp和esp两个寄存器为函数栈帧。其存放的为地址,用来维护函数栈帧。
压栈:给栈顶放一个元素。
出栈:给栈顶删除一个元素。
栈帧是由高地址移向低地址。
2.2.2定义:
我们在写C语言代码的时候,经常会把一个独立的功能抽象为函数,所以C程序是以函数为基本单位的。
那函数是如何调用的?函数的返回值又是如何待会的?函数参数是如何传递的?这些问题都和函数栈帧有关系。
函数栈帧(stack frame)就是函数调用过程中在程序的调用栈(call stack)所开辟的空间,这些空间是用来存放:
函数参数和函数返回值
临时变量(包括函数的非静态的局部变量以及编译器自动生产的其他临时变量)
保存上下文信息(包括在函数调用前后需要保持不变的寄存器)。
3递归函数:
有5个学生坐在一起,问第5个学生多少岁,他说比第4个学生大2岁。问第
4个学生岁数,他说比第3个学生大2岁。问第3个学生,又说比第2个学生大2岁。问第
2个学生,说比第1个学生大2岁。最后问第1个学生,他说是10岁。请问第5个学生
多大。
递归图像详解。
对应程序如下:
#include <stdio.h>
int get(int c)//调用函数get
{
int age;
if(c==1){
age=10;
}else {
age=getAge(c-1)+2;//运用递归求解
}
return age;
}
int main()
{
int age;
int num;
printf("你要知道第几个学生的年龄?\n");
scanf("%d",&num);
age=get(num);
printf("第%d个学生年龄为:%d\n",num,age);
return 0;
}
例如:数学函数也可以递归定义。例如,阶乘函数f(n)=n!可以定义为。
f(0)=1;f(n)=f(n-1)*n(n>=1)
对应的程序如下:
#include <stdio.h>
int Di(int a) {
int o = 0;
if (a == 1)
o = 1;
else
o = Di(a - 1) * a;
return o;
}
int main() {
int a = 0;
scanf("%d", &a);
int rew = Di(a);
printf("%d", rew);
return 0;
}