递归中对于参数和变量的理解



对于递归函数:参数,局部变量的生存期和调用时间问题

==================================================================================================

#include <stdio.h>

int binary_to_ascii( unsigned int value)

{

unsigned int quotient;

  quotient = value / 10;                               ---------------------------------》递归调用前的语句

  if( quotient != 0)

    binary_to_ascii( quotient);                                                                                                       

putchar ( value % 10 + ‘0‘ );                     ---------------------------------》递归调用后的语句

}

----------------------------------》思考他们的调用顺序

递归是如何帮助我们以正确的顺序打印这些字符呢?下面是这个函数的工作流程。

1. 将参数值除以10

2. 如果quotient的值为非零,调用binary-to-ascii打印quotient当前值的各位数字

  3. 接着,打印步骤1中除法运算的余数

  注意在第2个步骤中,我们需要打印的是quotient当前值的各位数字。我们所面临的问题和最初的问题完全相同,只是变量quotient的值变小了。我们用刚刚编写的函数(把整数转换为各个数字字符并打印出来)来解决这个问题。由于quotient的值越来越小,所以递归最终会终止。

  一旦你理解了递归,阅读递归函数最容易的方法不是纠缠于它的执行过程,而是相信递归函数会顺利完成它的任务。如果你的每个步骤正确无误,你的限制条件设置正确,并且每次调用之后更接近限制条件,递归函数总是能正确的完成任务。

  但是,为了理解递归的工作原理,你需要追踪递归调用的执行过程,所以让我们来进行这项工作。追踪一个递归函数的执行过程的关键是理解函数中所声明的变量是如何存储的。当函数被调用时,它的变量的空间是创建于运行时堆栈上的。以前调用的函数的变量扔保留在堆栈上,但他们被新函数的变量所掩盖,因此是不能被访问的。

  当递归函数调用自身时,情况于是如此。每进行一次新的调用,都将创建一批变量,他们将掩盖递归函数前一次调用所创建的变量。当我追踪一个递归函数的执行过程时,必须把分数不同次调用的变量区分开来,以避免混淆。

  程序中的函数有两个变量:参数value和局部变量quotient。下面的一些图显示了堆栈的状态,当前可以访问的变量位于栈顶。所有其他调用的变量饰以灰色的阴影,表示他们不能被当前正在执行的函数访问。

假定我们以4267这个值调用递归函数。当函数刚开始执行时,堆栈的内容如下图所示:

执行除法之后,堆栈的内容如下:

接着,if语句判断出quotient的值非零,所以对该函数执行递归调用。当这个函数第二次被调用之初,堆栈的内容如下:

堆栈上创建了一批新的变量,隐藏了前面的那批变量,除非当前这次递归调用返回,否则他们是不能被访问的。再次执行除法运算之后,堆栈的内容如下:

quotient的值现在为42,仍然非零,所以需要继续执行递归调用,并再创建一批变量。在执行完这次调用的出发运算之后,堆栈的内容如下:

堆栈上创建了一批新的变量,隐藏了前面的那批变量,除非当前这次递归调用返回,否则他们是不能被访问的。再次执行除法运算之后,堆栈的内容如下:

quotient的值现在为42,仍然非零,所以需要继续执行递归调用,并再创建一批变量。在执行完这次调用的出发运算之后,堆栈的内容如下:

此时,quotient的值还是非零,仍然需要执行递归调用。在执行除法运算之后,堆栈的内容如下:

  不算递归调用语句本身,到目前为止所执行的语句只是除法运算以及对quotient的值进行测试。由于递归调用这些语句重复执行,所以它的效果类似循环:当quotient的值非零时,把它的值作为初始值重新开始循环。但是,递归调用将会保存一些信息(这点与循环不同),也就好是保存在堆栈中的变量值。这些信息很快就会变得非常重要。

  现在quotient的值变成了零,递归函数便不再调用自身,而是开始打印输出。然后函数返回,并开始销毁堆栈上的变量值。

每次调用putchar得到变量value的最后一个数字,方法是对value进行模10取余运算,其结果是一个0到9之间的整数。把它与字符常量‘0’相加,其结果便是对应于这个数字的ASCII字符,然后把这个字符打印出来。

输出4:

接着函数返回,它的变量从堆栈中销毁。接着,递归函数的前一次调用重新继续执行,她所使用的是自己的变量,他们现在位于堆栈的顶部。因为它的value值是42,所以调用putchar后打印出来的数字是2。

输出42:

接着递归函数的这次调用也返回,它的变量也被销毁,此时位于堆栈顶部的是递归函数再前一次调用的变量。递归调用从这个位置继续执行,这次打印的数字是6。在这次调用返回之前,堆栈的内容如下:

输出426:

现在我们已经展开了整个递归过程,并回到该函数最初的调用。这次调用打印出数字7,也就是它的value参数除10的余数。

输出4267:

然后,这个递归函数就彻底返回到其他函数调用它的地点。

如果你把打印出来的字符一个接一个排在一起,出现在打印机或屏幕上,你将看到正确的值:4267

递归中对于参数和变量的理解

时间: 2024-12-13 15:57:23

递归中对于参数和变量的理解的相关文章

RGB颜色中的参数是变量的时候,为什么要加上两个+号在左右?

<script> function draw(){ var c=document.getElementById("mycanvas"); var cxt=c.getContext("2d"); for(var i=0;i<12;i++){ for(var j=0;j<24;j++){ cxt.fillStyle="rgb(240,"+Math.floor(255-11.5*i)+","+Math.flo

存储过程中使用参数和变量作为查询条件对性能的影响?

今日匆忙中写了2各存储过程,查询的表,查询的结构,返回的值基本一样,就是对参数的使用有点不同. 晚上到家想着优化下,看看能不能合并了.在几次重写后,拿着几个版本查看执行计划时,发现个以前没注意的问题. 第一个SP传入的2个时间参数直接用于里面的查询条件. 第二个SP传入的2个时间参数,在SP内又定义了2个时间变量,将参数通过简单计算后Set给了2个变量,然后在查询中使用这2个变量作为条件. 这2个SP,在执行开销上相差数倍. 在外面套一层SP,把时间算好了再当参数传给第二个SP,修改SP直接使用

c语言中对可变参数列表的简单理解

函数原型中一般情况下参数的数目是固定的,但是如果想在不同的时候接收不定数目的参数时该怎么办呢?c语言提供了可变参数列表来实现. 可变参数列表是通过宏来实现的,这些宏定义在stdarg.h的头文件中.头文件中声明了一个va_list类型和va_start.va_arg.va_end三个宏.我们使用可变参数列表的时候需要声明一个va_list类型的变量配合这三个宏使用. va_start(va_list变量名,省略号前面最后一个有名字的参数):在提取可变参数前必须调用这个宏实现初始化. va_arg

继承的基本概念: (1)Java不支持多继承,也就是说子类至多只能有一个父类。 (2)子类继承了其父类中不是私有的成员变量和成员方法,作为自己的成员变量和方法。 (3)子类中定义的成员变量和父类中定义的成员变量相同时,则父类中的成员变量不能被继承。 (4)子类中定义的成员方法,并且这个方法的名字返回类型,以及参数个数和类型与父类的某个成员方法完全相同,则父类的成员方法不能被继承。 分析以上程

继承的基本概念: (1)Java不支持多继承,也就是说子类至多只能有一个父类. (2)子类继承了其父类中不是私有的成员变量和成员方法,作为自己的成员变量和方法.(3)子类中定义的成员变量和父类中定义的成员变量相同时,则父类中的成员变量不能被继承.(4)子类中定义的成员方法,并且这个方法的名字返回类型,以及参数个数和类型与父类的某个成员方法完全相同,则父类的成员方法不能被继承. 分析以上程序示例,主要疑惑点是“子类继承父类的成员变量,父类对象是否会实例化?私有成员变量是否会被继承?被继承的成员变量

在ModelSim波形图中以参数名显示变量

在ModelSim波形图中以参数名显示变量 在使用Verilog HDL编写有限状态机等逻辑的时候,状态机的各个状态通常以参数表示,但当使用ModelSim仿真的时候,状态机变量在wave窗口中以二进制编码的形式显示,例如:4’h0.4’h1等.这种显示形式不是很直观,但我们可以使用ModelSim提供的命令将状态机变量以“文本”形式的参数名显示,从而有利于调试. 假如一个状态机有如下的编码: parameter  WAIT_INPUT1 = 2'b00,  // 状态机参数定义,表示4个状态

Swift中什么时候用变量参数、inout参数

首先要明白一般的常量参数:函数中的参数在函数内不能进行运算的参数是常数参数.苹果声明一个函数,对一个参数的默认行为就是这个参数是不允许修改的.默认参数是一个let值的参数. 变量参数:当传入参数时候,在函数体内可以修改这个传入的参数时,需要变量参数,变量参数需要在声明函数时参数处加上var,这也是其他语言参数的一般类型. inout参数:在函数内对参数进行修改之后,在函数外部参数如果也发生了变化,则这个参数为inout参数 //使用inout传入参数的值是切切实实会改变的 func swapTw

?Swift语言中为外部参数设置默认值,可变参数,常量参数,变量参数,输入输出参数

引用自这里:Swift语言中为外部参数设置默认值可变参数常量参数变量参数输入输出参数 目录[-] 7.4.4  为外部参数设置默认值 7.4.5  可变参数 7.4.6  常量参数和变量参数 7.4.7  输入-输出参数 7.4.4  为外部参数设置默认值 开发者也可以对外部参数设置默认值.这时,调用的时候,也可以省略参数传递本文选自Swift1.2语言快速入门v2.0. [示例7-11]以下的代码就为外部参数toString.withJoiner设置了默认的参数"Swift"和&qu

关于变量的理解和函数内参数的引用

首先理解变量包括两种不同数据类型的值:1.基本类型值 2.引用类型值 基本类型值为简单的数据段,如var name="tom": name.age=20: alert(name.age): //undefined  不能给基本类型的值无法添加属性. 引用类型值为可能由多个值构建的对象,如var person=new Object(): person.age=20: alert(person.age): //20  可以给基本类型的值添加属性. 函数内参数的传递:1.基本类型的值传递:f

Loadrunner中参数和变量的使用

Loadrunner中参数和变量的使用 //字符串复制 strcpy(str,"Hello ") ; //字符串连接 strcat(str,"World !"); lr_message("str: %s",str); //变量转为参数,将变量str的值存到参数Param中 lr_save_string(str,"Param"); //参数复制 lr_save_string(lr_eval_string("{Param