【转】深入理解递归函数的调用过程

下面是个关于递归调用简单但是很能说明问题的例子:

[cpp] view plain copy

  1. /*递归例子*/
  2. #include<stdio.h>
  3. void up_and_down(int);
  4. int main(void)
  5. {
  6. up_and_down(1);
  7. return 0;
  8. }
  9. void up_and_down(int n)
  10. {
  11. printf("Level %d:n location %p/n",n,&n); /* 1 */
  12. if(n<4)
  13. up_and_down(n+1);
  14. printf("Level %d:n location %p/n",n,&n); /* 2 */
  15. }

输出结果
Level 1:n location 0240FF48
Level 2:n location 0240FF28
Level 3:n location 0240FF08
Level 4:n location 0240FEE8
Level 4:n location 0240FEE8
Level 3:n location 0240FF08
Level 2:n location 0240FF28
Level 1:n location 0240FF48

 首先, main() 使用参数 1 调用了函数 up_and_down() ,于是 up_and_down() 中形式参数 n 的值是 1, 故打印语句 #1 输出了 Level1 。然后,由于 n 的数值小于 4 ,所以 up_and_down() (第 1 级)使用参数 n+1 即数值 2 调用了 up_and_down()( 第 2 级 ). 使得 n 在第 2级调用中被赋值 2, 打印语句 #1 输出的是 Level2 。与之类似,下面的两次调用分别打印出 Level3 和 Level4 。

 当开始执行第 4 级调用时, n 的值是 4 ,因此 if 语句的条件不满足。这时候不再继续调用 up_and_down() 函数。第 4 级调用接着执行打印语句 #2 ,即输出 Level4 ,因为 n 的值是 4 。现在函数需要执行 return 语句,此时第 4 级调用结束,把控制权返回给该函数的调用函数,也就是第 3 级调用函数。第 3 级调用函数中前一个执行过的语句是在 if 语句中进行第 4 级调用。因此,它继续执行其后继代码,即执行打印语句 #2 ,这将会输出 Level3 .当第 3 级调用结束后,第 2 级调用函数开始继续执行,即输出Level2 .依次类推.

 注意,每一级的递归都使用它自己的私有的变量 n .可以查看地址的值来证明.

递归的基本原理

1 每一次函数调用都会有一次返回.当程序流执行到某一级递归的结尾处时,它会转移到前一级递归继续执行.

2 递归函数中,位于递归调用前的语句和各级被调函数具有相同的顺序.如打印语句 #1 位于递归调用语句前,它按照递归调用的顺序被执行了 4 次.

3 每一级的函数调用都有自己的私有变量.

4 递归函数中,位于递归调用语句后的语句的执行顺序和各个被调用函数的顺序相反.

5 虽然每一级递归有自己的变量,但是函数代码并不会得到复制.

6 递归函数中必须包含可以终止递归调用的语句.

再看一个具体的递归函数调用的例子:以二进制形式输出整数

[cpp] view plain copy

  1. /*输入一个整数,输出二进制形式*/
  2. #include<stdio.h>
  3. void to_binary(unsigned long n);
  4. int main(void)
  5. {
  6. unsigned long number;
  7. printf("Enter an integer(q to quit):/n");
  8. while(scanf("%ul",&number)==1)
  9. {
  10. printf("Binary equivalent :");
  11. to_binary(number);
  12. putchar(‘/n‘);
  13. printf("Enter an integer(q to quit):/n");
  14. }
  15. printf("Done./n");
  16. return 0;
  17. }
  18. void to_binary(unsigned long n)    /*递归函数*/
  19. {
  20. int r;
  21. r=n%2;    /*在递归调用之前计算n%2的数值,然后在递归调用语句之后进行输出.这样
  22.       计算出的第一个数值反而是在最后一个输出*/
  23. if(n>=2)
  24. to_binary(n/2);
  25. putchar(‘0‘+r);/*如果r是0,表达式‘0‘+r就是字符‘0‘;如果r是1,则表达式的值为
  26.          ‘1‘.注意前提是字符‘1‘的数值编码比字符‘0‘的数值编码大1.
  27.          ASCII和EBCDIC这两种编码都满足这个条件.*/
  28. return;
  29. }

输出结果
Enter an integer(q to quit):
9
Binary equivalent :1001
Enter an integer(q to quit):
255
Binary equivalent :11111111
Enter an integer(q to quit):

FROM: http://blog.csdn.net/ysuncn/archive/2007/09/21/1793896.aspx

时间: 2024-10-13 08:58:10

【转】深入理解递归函数的调用过程的相关文章

深入理解递归函数的调用过程

转:深入理解递归函数的调用过程 下面是个关于递归调用简单但是很能说明问题的例子: 1 /*递归例子*/ 2 #include<stdio.h> 3 void up_and_down(int); 4 int main(void) 5 { 6 up_and_down(1); 7 return 0; 8 } 9 void up_and_down(int n) 10 { 11 printf("Level %d:n location %p/n",n,&n); /* 1 */

[Android Pro] 深入理解函数的调用过程——栈帧

cp :http://blog.csdn.net/x_perseverance/article/details/78897637 每一个函数被调用时,都会为函数开辟一块空间,这块空间就称为栈帧. 首先,我们了解一下不同种类的寄存器: (1)eax,ebx,ecx,edx :通用寄存器 (2)ebp:存放指向函数栈帧栈底的地址 (3)esp:存放指向函数栈帧栈顶的地址 (4)eip:程序计数器——保存程序当前正在执行指令的下一个指令的地址 接着我们以下面这段代码为例,深入到函数的调用过程中去: #

从涂鸦到发布——理解API的设计过程(转)

英文原文:From Doodles to Delivery: An API Design Process 要想设计出可以正常运行的Web API,对基于web的应用的基本理解是一个良好的基础.但如果你的目标是创建出优秀的API,那么仅凭这一点还远远不够.设计优秀的API是一个艰难的过程,如果它恰巧是你当前的工作任务,那么你很可能会感到手足无措. 不过,优秀的设计绝对是可以实现的.本文所描述的流程将帮助你获得成功,我们将共同研究什么是优秀的设计,以及迭代式的流程如何帮助我们实现这一目标.我们还将叙

Android学习Scroller(五)——详解Scroller调用过程以及View的重绘

MainActivity如下: package cc.ww; import android.os.Bundle; import android.widget.ImageView; import android.widget.ImageView.ScaleType; import android.widget.RelativeLayout; import android.widget.RelativeLayout.LayoutParams; import android.app.Activity;

使用IDA PRO+OllyDbg+PEview 追踪windows API 动态链接库函数的调用过程

http://bbs.pediy.com/showthread.php?p=1354999 标 题: [原创]使用IDA PRO+OllyDbg+PEview 追踪windows API 动态链接库函数的调用过程.作 者: shayi时 间: 2015-02-12,05:19:54链 接: http://bbs.pediy.com/showthread.php?t=197829 使用IDA PRO+OllyDbg+PEview 追踪windows API 动态链接库函数的调用过程. (本文同步更

堆栈桢的生成原理(调试器是如何生成堆栈的调用过程的)

一直比较好奇,调试器是如何生成堆栈的调用过程的,比如如下代码: int add(int a, int b){    return a + b;} int main(){    int c = add(1, 2);    system("pause"); return 0;} 调用Add时的堆栈截图如下: 调试器究竟是如何生成这个堆栈过程的呢? 我最初的理解调试器是根据EBP来生成该堆栈的,原理如下:可以看到按照上面的原理, 每次EBP里存放的都是当前函数的堆栈桢基址,所以我们只要一直递

理解递归函数,和动态规划

作为一个CS学生,必须理解递归函数.这学期的Foundation of Computer里有一章讲到了递归,突然间有了新感悟. 以前理解递归,总会在脑海里模拟递归过程,直到返回为止.但常常内存不够,大脑宕机……T.T ------------------------------------------------------------------------------------------------------------------------------------ 举两个例子,一下

函数递归调用过程中的调用堆栈的情况

为了加深对函数递归调用过程中的理解,本Demo程序特意在VS2008 C#控制台程序实现了阶乘的计算功能,用于观察函数递归调用过程中的调用堆栈的情况. 源码如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace RecursiveTset { class Program { //阶乘的定义:n!=n*(n-1)!,特别的,1!=1:0!=1 //阶乘的实

Servlet的调用过程_生命周期_继承结构

Servlet调用过程 浏览器上输入网站,dns解析域名成服务器的IP地址,浏览器带着一个请求行,和众多request请求头,找服务器. 1.分析请求的是哪台虚拟主机,查看Host请求头,如果没有Host请求头就访问缺省虚拟主机 2.分析访问的是当前虚拟主机中的哪个web资源,从请求行中分析出当前访问哪个web应用的资源 3.查找web.xml,查看有没有对应的虚拟路径 Servlet的生命周期 1.Servlet在第一次被访问到的时候,服务器创建出Servlet对象. 2.创建出对象之后,立即