b函数与运算符的重载.ppt

上传人:pu****.1 文档编号:568628828 上传时间:2024-07-25 格式:PPT 页数:106 大小:1.38MB
返回 下载 相关 举报
b函数与运算符的重载.ppt_第1页
第1页 / 共106页
b函数与运算符的重载.ppt_第2页
第2页 / 共106页
b函数与运算符的重载.ppt_第3页
第3页 / 共106页
b函数与运算符的重载.ppt_第4页
第4页 / 共106页
b函数与运算符的重载.ppt_第5页
第5页 / 共106页
点击查看更多>>
资源描述

《b函数与运算符的重载.ppt》由会员分享,可在线阅读,更多相关《b函数与运算符的重载.ppt(106页珍藏版)》请在金锄头文库上搜索。

1、第第5 5章章 函数与运算符的重载函数与运算符的重载5.1 5.1 .5.2 5.2 .5.3 5.3 函数的嵌套与递归函数的嵌套与递归5.4 5.4 函数与运算符的重载函数与运算符的重载5.5 5.5 函数与函数与C+C+程序结构程序结构5.6 5.6 程序实例程序实例1问题问题1 1,为什么要用函数,为什么要用函数2 2,使用函数的程序和顺序程序有什么,使用函数的程序和顺序程序有什么区别?区别?25.3.15.3.1函数的嵌套函数的嵌套函数的嵌套函数的嵌套一个函数的函数体中包含一个或多个函数调用语句,即称为函数嵌套一个函数的函数体中包含一个或多个函数调用语句,即称为函数嵌套 。n嵌套的含义

2、是,如果函数A 要调用函数B,也就是说,函数A 的定义要依赖于函数B 的定义。因此函数B 的定义或函数B 的原型必须出现在函数A 的定义语句之前。n另一方面,函数A 调用函数B,在调用A 的过程中,即执行A 的函数体过程中,调用B,也就是中途把程序控制转到B 的函数体,在执行结束后再返回到A 的函数体中。n函数嵌套调用所占用的空间(如赋值参数的创建等等)用堆栈(stack)的方式管理。一般这种堆栈所分配的空间是有限的,因此函数互相嵌套的层数也是有限的,依编译系统不同,其允许的嵌套层数也可能不同。 3 函数调用的堆栈情况函数调用的堆栈情况 堆栈堆栈Main()cuberoot( x ) 参数传递

3、、返回值参数传递、返回值保护现场、恢复现场保护现场、恢复现场调用调用返回返回4实例实例# include void f1 (int,int); void f2 (int); void main () int a, b ; cout a ; cout b; f1 (a, a+b); cout endl a+b endl; void f1 (int x, int y) int m = 2; x *= m ;y+; f2 (x+y)/m);void f2 (int p) if (p = 100) cout endl 1n1时,时, n!=n*(n-1)!n!=n*(n-1)!这正是我们编写求这正是我

4、们编写求n n的阶乘的递归函数的阶乘的递归函数prodprod的基础。的基础。9#include #include long long prod(intprod(int n) / n) /注意用的是注意用的是longlong,课本课本p131p131(3 3) if ( n=1 ) if ( n=1 ) return return 1; 1; /n/n等等于于1 1时时,递递归归出出口口(“(“退退出出”递递归归) ) else elsereturn n * return n * prod(n-1)prod(n-1); ; / n/ n大于大于1 1时的返回值时的返回值( (n!)n!)为为n

5、 n乘以乘以n-1n-1的阶乘的阶乘/ ( / ( 使用自递归调用使用自递归调用“prod(n-1)prod(n-1)”来求出来求出( (n-1)! )n-1)! )10void main( ) void main( ) intint n; n; coutcoutInput a positive integer:;n; n; / /输入的正整数放入输入的正整数放入n n中中 long p=prod(n); /long p=prod(n); /求出求出n n的阶乘放入的阶乘放入p p中中 coutcoutp=1*.*n=pp=1*.*n=pendlendl; ; /输出结果输出结果p p 11

6、上上述述求求阶阶乘乘的的递递归归函函数数中中,当当主主函函数数通通过过prod(3)prod(3)对对 递递 归归 函函 数数 prodprod进进 行行 调调 用用 时时 , 它它 的的 返返 回回 值值 为为3*3*prod(2)prod(2),此此时时系系统统将将再再一一次次对对prodprod本本身身进进行行调调用用而形成递归调用。而形成递归调用。 但但注注意意后后一一次次调调用用的的实实参参为为2 2,比比上上一一次次的的实实参参3“3“下下降降”了了1 1。而而计计算算prod(2)prod(2)时时,它它的的返返回回值值为为2*2*prod(1)prod(1),此此时时系系统统将

7、将再再一一次次对对prodprod本本身身进进行行递递归归调用,但此时的实参又调用,但此时的实参又“下降下降”了了1 1。12 正正是是通通过过这这种种实实参参的的逐逐次次“下下降降”,可可保保障障递递归归函函数数在在执执行行若若干干次次后后( (此此时时的的求求阶阶乘乘问问题题当当“下下降降”到到1 1时时) ),能能够够“退退出出”递递归归( (不不再再进进行行递递归归调调用,也即实现了递归出口用,也即实现了递归出口) )。 由由于于prod(1)prod(1)的的返返回回值值为为1 1,系系统统进进一一步步算算出出2*2*prod(1)prod(1)的的值值(即即prod(2)prod(

8、2)的的值值)为为2*1=22*1=2,再再进进一一步步算算出出3*3*prod(2)prod(2)的的值值为为3*2=63*2=6,这这正正是是prod(3)prod(3)的调用返回值的调用返回值( (也即求出了也即求出了3!=3*2*1=6)3!=3*2*1=6)。 13 虽虽然然上上述述执执行行过过程程是是由由系系统统自自动动完完成成的的,但但程程序序员员要要理理解解并并熟熟知知这这种种调调用用机机制制与与系系统统实实现现方方法法,从从而而才才能能编编写写出出逻逻辑辑正正确确且且简简明明易易懂懂的的递递归归处处理理程程序。序。 另另外外注注意意,递递归归处处理理程程序序的的执执行行速速度

9、度通通常常要要比比非递归处理方法慢。非递归处理方法慢。问题:为什么会慢?问题:为什么会慢? 14 求出求出1 1到到n n之累加和的递归函数之累加和的递归函数sumsum 使程序执行后的输出结果为使程序执行后的输出结果为: :Input a positive integer:Input a positive integer:100100s=1+.+100=5050s=1+.+100=505015# #include include intint sum(intsum(int n) n) /递归函数递归函数sumsum if ( n=1 ) if ( n=1 ) /n/n等于等于1 1时,递归出

10、口时,递归出口return 1;return 1; else else return n + return n + sum(n-1)sum(n-1); ; /n/n大于大于1 1时的返回值时的返回值( (累加和累加和) )为为n n加上加上“从从1 1累加到累加到/n-1/n-1的的和和”(”(要要使使用用递递归归调调用用求求出出前前n-1n-1个个数数的的累累加加和和) )16 void main( ) void main( ) intint n; n; coutcoutInput a positive integer:;n; n; intint s=sum(n); / s=sum(n);

11、/求出从求出从1 1累加到累加到n n的和放的和放s s中中 coutcouts=1+.+n=ss=1+.+n=sendlendl; ; 172 2 反序输出从键盘输入的反序输出从键盘输入的1010个整数个整数 反反序序输输出出:从从键键盘盘输输入入10 10 个个intint 型型数数,而后按输入的相反顺序输出它们。而后按输入的相反顺序输出它们。例如:例如:输入:输入:1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 输出:输出:10 9 8 7 6 5 4 3 2 110 9 8 7 6 5 4 3 2 118 2 2 反序输出从键盘输入的反序输出从键盘输

12、入的1010个整数个整数# include void inv (int n) int i; cini;/输入输入if (n1) inv (n-1); /递归递归 else cout“- The result -”endl; /递归出口递归出口 cout i “ ”; /输出输出void main(void) coutInput 10 integers:endl;inv(10); coutendl; 19 2 2 反序输出从键盘输入的反序输出从键盘输入的1010个整数个整数n可以有下面的执行结果:可以有下面的执行结果:nInput 10 integers:Input 10 integers:1

13、2 3 4 5 6 7 8 9 101 2 3 4 5 6 7 8 9 10- The result - The result -10 9 8 7 6 5 4 3 2 110 9 8 7 6 5 4 3 2 120 3 3 反序输出一个整数反序输出一个整数 在许多情形下递归函数易写易读,像解著名在许多情形下递归函数易写易读,像解著名的的Hanoi Hanoi 塔问题的递归函数,其递归程序很塔问题的递归函数,其递归程序很短,也极易理解短,也极易理解( (见见5.6 5.6 节节) ),但是,如果不,但是,如果不用递归方法,程序将十分复杂,很难编写。用递归方法,程序将十分复杂,很难编写。要求要求

14、反序输出一个正整数的各位数值,反序输出一个正整数的各位数值, 如输入如输入231231,应输出,应输出132132。 21 3 3 反序输出一个整数反序输出一个整数# include int conv(int n) if (n10) coutn; return; /递归出口递归出口 cout n%10; conve(n/10); /递归递归 void main(void) int t;cout t; cout endl; conv(t);22 3 3 反序输出一个整数反序输出一个整数输出结果为:Input a positive number: 47811874如果不用递归函数设计,程序不如递归

15、形式清晰: int conv(int n) if (n0) cout “Please input a positive number!”;else do cout n%10;n=/10; while (n!=0); 23 总结总结(1) (1) 无论是直接递归还是间接递归都必须保证在有无论是直接递归还是间接递归都必须保证在有限次调用之后能够结束。例如函数限次调用之后能够结束。例如函数facfac 中的参数中的参数n n 在递归调用中每次减在递归调用中每次减1 1,总可达到,总可达到2 2 的状态而的状态而结束。结束。(2) (2) 函数调用时系统要付出时间和空间代价,在环函数调用时系统要付出时

16、间和空间代价,在环境条件相同的情形下,总是非递归程序效率较高。境条件相同的情形下,总是非递归程序效率较高。 24 5.4 5.4 函数和运算符的重载函数和运算符的重载5.4.1 5.4.1 函数重载函数重载5.4.2 5.4.2 可重载运算符可重载运算符5.4.3 5.4.3 运算符重载函数的定义运算符重载函数的定义25 5.4.1 5.4.1 函数的重载函数的重载 函数重载实际上是函数名重载,即支持多个不同的函数采用同一函数重载实际上是函数名重载,即支持多个不同的函数采用同一名字。名字。 例如:例如: intint abs abs(intint n n)returnreturn(n n0 0

17、?-n-n:n n; float abs float abs(float ffloat f)ifif(f f0 0)f f-f-f; return freturn f; double abs double abs(double ddouble d)ifif(d d0 0)return-dreturn-d;return dreturn d; 三个函数都是求绝对值,采用同一个函数名,更符合人们的习惯三个函数都是求绝对值,采用同一个函数名,更符合人们的习惯 . .例如在程序中经常出现这样的情况:对若干种不同的数据类型求和,例如在程序中经常出现这样的情况:对若干种不同的数据类型求和,虽然数据本身差别很

18、大(例如整数求和,向量求和,矩阵求和)虽然数据本身差别很大(例如整数求和,向量求和,矩阵求和),具体的求和操作差别也很大,但完成不同求和操作的函数却,具体的求和操作差别也很大,但完成不同求和操作的函数却可以取相同的名字(例如可以取相同的名字(例如sumsum,add add 等)。打印函数等)。打印函数printprint,显,显示函数示函数displaydisplay等也是同样。等也是同样。 26 5.4.1 5.4.1 函数的重载函数的重载 函数名的重载并不是为了节省标识符(标识函数名的重载并不是为了节省标识符(标识符的数量是足够的),而是为了方便程序员符的数量是足够的),而是为了方便程序

19、员的使用,这一点很重要。实现函数的重载必的使用,这一点很重要。实现函数的重载必须满足下列条件之一:须满足下列条件之一:n(1 1)参数表中对应的参数类型不同;)参数表中对应的参数类型不同;n(2 2)参数表中参数个数不同;)参数表中参数个数不同;n(3 3)参数表中不同类型参数的次序不同。)参数表中不同类型参数的次序不同。27 5.4.1 5.4.1 函数的重载函数的重载例如:例如: void printvoid print(intint);); /整型整型 void print void print(pointpoint);); /类类point point 的对象的对象 intint su

20、m sum(intint ,intint);); intint sum sum(intint ,intint ,intint);); intint get get(intint n n,float a float a );); intint get get(intint n n,float a float a ,intint n n);); 28 5.4.1 5.4.1 函数的重载函数的重载在定义同名函数时应注意:在定义同名函数时应注意:n(1 1)返回类型不能区分函数,)返回类型不能区分函数, float addfloat add(intint float float);); intint

21、add add(intint float float););/错误错误 n(2 2)采用引用参数不能区分函数,)采用引用参数不能区分函数,void printvoid print(doubledouble);); void printvoid print(doubledouble); /); /错误错误 n(3 3)有些派生基本类型的参数虽然可以区分同名函数,但)有些派生基本类型的参数虽然可以区分同名函数,但在使用中必须注意在使用中必须注意 n(4 4)包含可缺省参数时,可能造成二义性,)包含可缺省参数时,可能造成二义性, 29 5.4.1 5.4.1 函数的重载函数的重载nC+C+对函数重载

22、的处理过程:对函数重载的处理过程:(1 1)通过数组名与指针变量,函数名与函数指针,某类型变)通过数组名与指针变量,函数名与函数指针,某类型变量与量与const const 常量之间的转换,再查是否可实现匹配;常量之间的转换,再查是否可实现匹配;(2 2)把实参类型从低到高(按字长由短到长)进行基本类型)把实参类型从低到高(按字长由短到长)进行基本类型及其派生类型的转换,再检查是否可匹配;及其派生类型的转换,再检查是否可匹配; (3 3)查有无已定义的可变个数参数的函数,如有把它归为该)查有无已定义的可变个数参数的函数,如有把它归为该函数。函数。在进行上述尝试性的处理之后可能出现仍无匹配或匹配

23、不唯在进行上述尝试性的处理之后可能出现仍无匹配或匹配不唯一的情况,这时可能输出出错信息或错误地运行。一的情况,这时可能输出出错信息或错误地运行。 30 5.4.2 5.4.2 可重载运算符可重载运算符C+C+语言中的运算符实际上是函数的方便表示形式,语言中的运算符实际上是函数的方便表示形式,例如,算术运算符例如,算术运算符”+” +” 也可以表示为函数形式:也可以表示为函数形式:intint add ( add (intint a, a, intint b) return b) return a+ba+b; ; 这时,这时,a+ba+b 和和 add (add (a,ba,b) ) 的含义是一

24、样的。的含义是一样的。 C+C+语言规定,大多数运算符都可以重载,语言规定,大多数运算符都可以重载,单目运算符:单目运算符:- -, ,!,!,+,-,newnew,deletedelete双目运算符:双目运算符: + +,- -,* *, , , , = =,!=!=, ,=- ,= = , , 等等 31 5.4.2 5.4.2 可重载运算符可重载运算符(1 1)可重载运算符几乎包含了)可重载运算符几乎包含了C+C+的全部运算符集,例外的是:限定符的全部运算符集,例外的是:限定符. .,:,条件运算符?:,取长度运算符,:,条件运算符?:,取长度运算符sizeofsizeof 它们不可重载

25、它们不可重载 (2 2)在可重载的运算符中有几种不同情况:)在可重载的运算符中有几种不同情况: 算术运算符,逻辑运算符,位运算符等与基本数据类型有关,通过运算术运算符,逻辑运算符,位运算符等与基本数据类型有关,通过运算苻重载函数的定义,使它们可以用于某些用户定义的数据类型,算苻重载函数的定义,使它们可以用于某些用户定义的数据类型,这是重载的主要目的。这是重载的主要目的。赋值运算符,关系运算符,!等所涉及的数据类型按赋值运算符,关系运算符,!等所涉及的数据类型按C+C+程序程序规定,并非只限于基本数值类型。因此,这些运算符可以自动地扩规定,并非只限于基本数值类型。因此,这些运算符可以自动地扩展到

26、任何用户定义的数据类型,一般不需作重载定义就可展到任何用户定义的数据类型,一般不需作重载定义就可“自动自动”地实现重载。地实现重载。 单目运算符单目运算符+和和-实际上各有两种用法,前缀增(减)量和后缀增实际上各有两种用法,前缀增(减)量和后缀增(减)量。其运算符重载函数的定义当然是不同的,对两种不同的(减)量。其运算符重载函数的定义当然是不同的,对两种不同的运算无法从重载函数的原型上予以区分:函数名(运算无法从重载函数的原型上予以区分:函数名(operator +operator +)和)和参数表完全一样。为了区别前缀参数表完全一样。为了区别前缀+和后缀和后缀+,C+C+语言规定,在后缀语言

27、规定,在后缀+的重载函数的原型参数表中增加一个的重载函数的原型参数表中增加一个intint 型的无名参数。型的无名参数。 32 5.4.3 5.4.3 运算符重载函数的定义运算符重载函数的定义 运算符的重载是一个特殊函数定义过程,这类函运算符的重载是一个特殊函数定义过程,这类函数总是以数总是以operatoroperator 作为函数名。其实例作为函数名。其实例在第七章以后引进在第七章以后引进 假设程序中定义了一个枚举类型的bool 类型: enum boolFALSE,TRUE; 用运算符(双目),*(双目),(单目)来表示或、与、非运算是十分方便的:33 5.4.3 5.4.3 运算符重载

28、函数的定义运算符重载函数的定义bool operator + (bool a ,bool b) if(aFALSE)()(bFALSE) return FALSE; return TRUE; bool operator*(bool a,bool b) if(aTRUE)()(bTRUE) return TRUE; return FALSE; bool operator-(bool a) if(aFALSE) return TRUE; return FALSE; 我们可以在程序中方便的表我们可以在程序中方便的表示其运算:示其运算: b1b1b1+b2b1+b2; b1b1-b3-b3; b1b1

29、(b1+b3b1+b3)* * FALSEFALSE;34 5.4.3 5.4.3 运算符重载函数的定义运算符重载函数的定义运算符重载函数的调用可有两种方式:运算符重载函数的调用可有两种方式:1 1 与原运算符相同的调用方式,如上例中的与原运算符相同的调用方式,如上例中的b1+b2b1+b2,b1*b2b1*b2,等等。,等等。2 2 一般函数调用方式,如一般函数调用方式,如b1+b2b1+b2,也可以写为,也可以写为operator+operator+(b1b1,b2b2)被重载的运算符的调用方式,优)被重载的运算符的调用方式,优先级和运算顺序都与原运算符一致,其运算分量的个先级和运算顺序都

30、与原运算符一致,其运算分量的个数也不可改变。数也不可改变。3 3 运算符重载主要用于用类的形式定义的用户定义类型,运算符重载主要用于用类的形式定义的用户定义类型,例如,复数类型,集合类型,向量类型等等,通过运例如,复数类型,集合类型,向量类型等等,通过运算符重载把人们习惯的运算符引入到计算操作之中,算符重载把人们习惯的运算符引入到计算操作之中,会收到很好的效果。这样的实例将在第七章介绍。会收到很好的效果。这样的实例将在第七章介绍。 35 5.5 5.5 函数与函数与C+C+程序结构程序结构5.5.15.5.1库函数的使用库函数的使用 库函数又称标准函数,是库函数又称标准函数,是C+C+语言编译

31、系统为用语言编译系统为用户提供的内部函数,其编写与一般用户定义的户提供的内部函数,其编写与一般用户定义的函数相同,程序员可在程序中直接使用,但是函数相同,程序员可在程序中直接使用,但是要在程序开头说明库函数所在的头文件名,例要在程序开头说明库函数所在的头文件名,例如:如: #include #include #include #include C+系统中有一个很大的标准函数库(和标准类库),包括许多在各种程序中常用的基本任务处理函数,这些库函数被分成不同的组,例如,数学计算、字符处理、字符串处理、I/O操作、图形处理等等, 36 5.5.2 SP5.5.2 SP框架结构框架结构 按结构程序设计

32、(按结构程序设计(SPSP)思想设计的程序结构称为)思想设计的程序结构称为SPSP框框架。函数是架。函数是SPSP框架的核心。框架的核心。 一个一个SPSP框架的完整框架的完整C+C+程序由下面几部分组成:程序由下面几部分组成:n1 1)一个主函数。它可调用其它函数,但不能被调用。)一个主函数。它可调用其它函数,但不能被调用。n2 2)任意多个用户定义函数。都处于同一)任意多个用户定义函数。都处于同一“等级等级”,可,可以互相调用。以互相调用。n3 3)全局说明。在所有函数定义之外的变量说明和函数)全局说明。在所有函数定义之外的变量说明和函数原型。原型。n4 4)预处理命令。在进行预处理后,这

33、部分被取代。)预处理命令。在进行预处理后,这部分被取代。n5 5)注释。只起方便阅读的作用,编译后被删除。)注释。只起方便阅读的作用,编译后被删除。37 5.5.2 SP5.5.2 SP框架结构框架结构 对于比较大的程序,可以把它们划分为几个程序文件,对于比较大的程序,可以把它们划分为几个程序文件,这些程序模块可能由一个或多个程序员编写,最简单这些程序模块可能由一个或多个程序员编写,最简单而有效的划分方法是:而有效的划分方法是:n 根据主函数和各用户定义的函数的功能及相互关根据主函数和各用户定义的函数的功能及相互关系,把它们划分成若干个系,把它们划分成若干个.CPP.CPP文件。文件。n 按与

34、每个按与每个.CPP .CPP 程序文件中的函数有关的全局说明程序文件中的函数有关的全局说明组成一个或多个组成一个或多个.h.h(头)文件。(头)文件。n 程序中使用的库函数组成的若干程序中使用的库函数组成的若干.CPP .CPP 文件和对应文件和对应的的.h .h 文件。在预处理命令的帮助下,一个文件。在预处理命令的帮助下,一个C+C+程序被程序被划分为若干划分为若干.CPP.CPP和和.h.h程序文件。在包含命令的帮助下,程序文件。在包含命令的帮助下,这些文件形成了一个有机的整体。这些文件形成了一个有机的整体。n在这样的模块结构中,各个在这样的模块结构中,各个.CPP.CPP文件是全部函数

35、的划文件是全部函数的划分,它们组成了程序代码的主体。分,它们组成了程序代码的主体。38 5.5.5.3 5.3 函数间的数据传递函数间的数据传递 1. 1. 通过赋值参数和返回语句(单向)通过赋值参数和返回语句(单向) 2 2通过全局变量(双向)通过全局变量(双向) 3.3.通过指针类型参数和引用参数(双向)通过指针类型参数和引用参数(双向) 4.4.函数的数组类型参数(双向)函数的数组类型参数(双向)391 1 通过赋值参数通过赋值参数(“(“单向传递单向传递”方式方式) ) 传值方向只是:传值方向只是:“上层上层” =“” =“下层下层”。也即,。也即,可从主调函数可从主调函数A A中通过

36、赋值参数所对应的实参将值中通过赋值参数所对应的实参将值“传入传入”到被调函数到被调函数B B内内( (去使用去使用) ),但不可将被调函数,但不可将被调函数B B内改变后的参数值内改变后的参数值“传出传出”到主调函数到主调函数A A中中( (去接着去接着使用使用) )。系统处理方式为。系统处理方式为: : 被调函数中对形参值的改被调函数中对形参值的改变不影响主调函数处的任一变量的值变不影响主调函数处的任一变量的值( (形参分配有自形参分配有自己的局部于被调函数的存储空间,调用入口处将实己的局部于被调函数的存储空间,调用入口处将实参表达式的值赋给该局部变量参表达式的值赋给该局部变量) )。 40

37、 通过函数返回值通过函数返回值(“(“单向传递单向传递”方式方式) ) 通过函数内使用的通过函数内使用的returnreturn语句语句, , 可将被调函数可将被调函数B B内计算出的最终值内计算出的最终值“传传出出”到主调函数到主调函数A A的的“调用点调用点”处处( (去去接着使用接着使用) )。也即,传值方向只是:。也即,传值方向只是:“下层下层” =“” =“上层上层”。 41 全局变量的定义域可延续到整个程序执行结束,因此,只全局变量的定义域可延续到整个程序执行结束,因此,只要在函数中没有把该全局变量名说明为其它变量,在所有的要在函数中没有把该全局变量名说明为其它变量,在所有的函数中

38、都可以直接访问它,也就是说,函数间的数据传递还函数中都可以直接访问它,也就是说,函数间的数据传递还可以通过全局变量实现,这种传递可以是双向的可以通过全局变量实现,这种传递可以是双向的 可从主调函数可从主调函数A A中通过全局变量将值中通过全局变量将值“传入传入”到到被调函数被调函数B B内内( (在在A A中赋值中赋值, , 进入进入B B内后使用该值内后使用该值) ),又,又可将被调函数可将被调函数B B内改变后的全局变量值内改变后的全局变量值“传出传出”到主到主调函数调函数A A中中( (去接着使用去接着使用) )。也即,传值方向可为:。也即,传值方向可为:“上层上层” =“” =“下层下

39、层”,“下层下层” =“” =“上层上层”。2 通过全局变量通过全局变量(“双向传递双向传递”方式方式)42 3 3 通过引用参数通过引用参数(“(“双向传递双向传递”方式,方式, 有关引用的其它使用方法详见第有关引用的其它使用方法详见第6 6章章) ) 传值方向可为:传值方向可为:“上层上层” =“” =“下层下层”,“下下层层” =“” =“上层上层”。即是说,它不仅可向被调函数。即是说,它不仅可向被调函数的形参的形参“传入传入”值值( (调用时的实参值调用时的实参值) ),而且还可,而且还可通过该形参通过该形参“传出传出”值。系统处理方式为值。系统处理方式为: : 被调被调函数中对形参值

40、的使用与改变,就是对主调函数函数中对形参值的使用与改变,就是对主调函数中调用语句处所对应实参变量值的直接使用与改中调用语句处所对应实参变量值的直接使用与改变变( (形参不具有自己的局部于被调函数的存储空间,形参不具有自己的局部于被调函数的存储空间,它只是实参变量的一个它只是实参变量的一个“替身替身”)”)。43 4 4 通过数组参数或指针参数通过数组参数或指针参数(“(“双向传递双向传递” 方式,指针参数的具体使用方法见第方式,指针参数的具体使用方法见第6 6章章) ) 传值方向可为:传值方向可为:“上层上层” =“” =“下层下层”,“下层下层” =“” =“上层上层”。 数组作形参,且数组

41、作形参,且在被调函数内使用或改变在被调函数内使用或改变数组元素数组元素的值的值。系统处理方式为。系统处理方式为: : 对形参数组对形参数组元素的使用与改变,就是对实参数组元素的直元素的使用与改变,就是对实参数组元素的直接使用与改变。接使用与改变。 指针作形参,且指针作形参,且在被调函数内使用或改变在被调函数内使用或改变指针所指变量指针所指变量的值的值。系统处理方式为。系统处理方式为: : 被调函被调函数中对形参指针所指变量值的使用与改变,就数中对形参指针所指变量值的使用与改变,就是对实参指针所指变量值的直接使用与改变。是对实参指针所指变量值的直接使用与改变。44 数组可作为函数参数,从而把主调

42、函数数组可作为函数参数,从而把主调函数中的整个数组中的整个数组( (实际上是数组的首地址实际上是数组的首地址) )传给传给了被调函数。了被调函数。而后可在被调函数中而后可在被调函数中使用使用或或改改变变传来的那些数组元素的值传来的那些数组元素的值。但注意,若在。但注意,若在被调函数内改变数组元素值的话,返回主调被调函数内改变数组元素值的话,返回主调函数后,相应实参数组元素的值也进行了相函数后,相应实参数组元素的值也进行了相同的改变(同的改变(“双向传值双向传值”功能)。功能)。 数组参数数组参数45例例1. 1. 读如下程序,看执行读如下程序,看执行后会显示出什么结果后会显示出什么结果? ?#

43、 #include include void void ProcessingFunc(intProcessingFunc(int b, b, intint num); / num); /函数原型函数原型/intint型数组形参型数组形参b b,通常省去对元素个数的指定通常省去对元素个数的指定/(/(当然也可以进行指定当然也可以进行指定!)!)void main() void main() intint A10=0,1,2,3,4,9,8,7,66,88; A10=0,1,2,3,4,9,8,7,66,88;coutcout- before calling, ai= -n;- before ca

44、lling, ai= -n;for(intfor(int i=0; i10; i+) i=0; i10; i+)coutcoutAi ; Ai ; coutcoutendlendl; ;46 ProcessingFunc(AProcessingFunc(A, 10); /, 10); /函数调用函数调用coutcout- after calling, ai= - after calling, ai= -n;n;for(i=0; i10; i+)for(i=0; i10; i+) coutcoutAi ; Ai ; coutcoutendlendl; ; 47 void void Process

45、ingFunc(intProcessingFunc(int b, b, intint num) num) /数组形参数组形参b b,省去了对元素个数的指定省去了对元素个数的指定! !coutcout- in - in ProcessingFuncProcessingFunc, bi= -n; , bi= -n; for(intfor(int i=0; inum; i+) i=0; inum; i+)coutcoutbi ; bi ; coutcoutendlendl; ;intint tmptmp=b0;=b0; b0=bnum-1;b0=bnum-1; bnum-1=bnum-1=tmptm

46、p; ; b1+=100;b1+=100; 48程序执行后的显示结果如下:程序执行后的显示结果如下:- - before calling, ai= -before calling, ai= -0 1 2 3 4 9 8 7 66 880 1 2 3 4 9 8 7 66 88- in - in ProcessingFuncProcessingFunc, bi= -, bi= -0 1 2 3 4 9 8 7 66 880 1 2 3 4 9 8 7 66 88- after calling, ai= - after calling, ai= -88 101 2 3 4 9 8 7 66 088

47、 101 2 3 4 9 8 7 66 049例例2. 2. 求求a a数组中前数组中前n n个整数个整数累加和的递归函数累加和的递归函数sumsum其中使用了数组形参与递归函数。其中使用了数组形参与递归函数。程序执行后的输出结果为程序执行后的输出结果为: :Input 6 integers:Input 6 integers:22 4 -2 9 100 322 4 -2 9 100 3s=136s=13650#include #include intint sum(intsum(int a, a, intint n) n) if ( n=1 )if ( n=1 ) return a0; ret

48、urn a0;elseelse return ( an-1 + return ( an-1 + sum(a, n-1)sum(a, n-1) ); ); 51 void main()void main()const const intint n=6; n=6;intint An; An; coutcoutInput n integers:Input n integers:endlendl; ;for(intfor(int i=0; in; i+) i=0; iAi;Ai;intint s = sum(A, n); s = sum(A, n);coutcouts=ss=sendlendl; ;

49、52 程序中出现的所有名字(标识符)都必须说明,每程序中出现的所有名字(标识符)都必须说明,每个名字(变量名、常量名、参数名、函数名、类名、个名字(变量名、常量名、参数名、函数名、类名、对象名等)都在程序的一定范围内有意义,就是该对象名等)都在程序的一定范围内有意义,就是该名字的作用域。名字的作用域。 1. 1. 外部存储属性与静态存储属性外部存储属性与静态存储属性2 2名字的生存期与作用域名字的生存期与作用域5.5.4 5.5.4 变量与函数的作用域变量与函数的作用域53 一一个个C+C+程程序序由由一一个个主主函函数数和和若若干干用用户户定定义义函函数数(或或类类)组组成成,当当程程序序的

50、的规规模模较较大大时时,整整个个程程序序可可能能被被划划分分为为几几个个程程序序文文件件,关关键键字字extern(extern(外外部部存存储储属属性性) )和和static(static(静静态态存存储储属属性性) )可可以以规规定定所所说说明明的的变变量量名名或或函函数数名名的的作作用用域域在在一一个个程程序序文文件件范范围围内内还还是是扩展到程序文件之外。扩展到程序文件之外。 外部存储属性与静态存储属性外部存储属性与静态存储属性54 C+ C+的存储类别的存储类别 存存储储类类别别主主要要是是针针对对变变量量而而言言的的。变变量量不不仅仅具具有有数数据据类类型型(存存储储空空间间大大小

51、小), , 而而且且还还具具有有存存储储类类别(所占存储空间的期限,即生命期)。别(所占存储空间的期限,即生命期)。 变变量量的的存存储储类类别别可可分分为为以以下下四四种种:自自动动( (auto)auto)型型、寄寄存存器器( (register)register)型型、外外部部( (extern)extern)型型、静静态态( (static)static)型。型。 55 使用显式存储类别时,变量说明的一般格式变为:使用显式存储类别时,变量说明的一般格式变为: , 1, . . , , ;n; 其其 中中 的的 “”可可 以以 是是 autoauto、 registerregister、

52、externextern、staticstatic四个关键字之一。四个关键字之一。 C+C+程程序序的的数数据据主主要要存存放放在在如如下下两两个个数数据据区区之之中中,一一个个称称为为静静态态数数据据区区(也也称称全全局局数数据据区区 - - “一一旦旦分分配配,一一直直拥拥有有”),另另一一个个称称为为动动态态数数据据区区(也也称称堆堆栈栈数数据据区区 - “- “入时分配并拥有,出时归还两手空入时分配并拥有,出时归还两手空”)。)。56 具具有有程程序序级级作作用用域域以以及及文文件件级级作作用用域域的的那那些些变变量量被被分分配配在在静静态态数数据据区区之之中中。另另外外,具具有有st

53、aticstatic存存储储类类别别的的变变量量也也均均被被分分配配在在静静态态数数据据区区之之中中。在在程程序序执执行行时时,就就为为这这种种变变量量分分配配空空间间并并进进行行隐隐式式初初始始化化(将将数数值值量量初初始始化化为为0 0,将将字字符符量量初初始始化化为为空空格格),直直到到程程序序执执行行结结束束时时才才释释放放这这些些存存储空间。储空间。 具具有有其其它它局局部部作作用用域域的的那那些些变变量量被被分分配配在在动动态态数数据据区区之之中中。在在程程序序执执行行时时,每每遇遇这这种种变变量量的的作作用用域域开开始始时时就就为为其其分分配配一一次次存存储储空空间间(但但并并不

54、不进进行行隐隐式式初初始始化化),而而后后, , 在在每每遇遇这这种种变变量量的的作作用用域域结结束束时时就就立立即即释释放放它们所占据的存储空间。它们所占据的存储空间。 57 变量的存储类别变量的存储类别 (1) (1) 自动(自动(autoauto)变量与寄存器(变量与寄存器(registerregister)变量变量 这这两两类类变变量量都都是是局局部部变变量量。均均被被分分配配在在动动态态数数据据区区之之中中。它它们们的的“生生命命期期”仅仅在在其其作作用用域域之之内内。即即是是说说,这这两两类类变变量量的的作用域与作用域与“生命期生命期”具有一致性。具有一致性。 由由于于C+C+编编

55、译译器器默默认认所所有有在在函函数数或或块块内内说说明明的的局局部部变变量量均均为为自自动动(autoauto)变变量量, , 所所以以实实用用程程序序中中根根本本不不需需要要使使用用关关键键字字autoauto。 说说明明寄寄存存器器(registerregister)变变量量时时,必必须须使使用用存存储储类类别别registerregister。如如果果没没有有足足够够多多的的通通用用寄寄存存器器,则则编编译译器器将将它它们们按按自动(自动(autoauto)变量来处理。变量来处理。 58 (2) (2) 外部(外部(externextern)变量变量 外外部部(externextern)

56、变变量量在在所所有有的的函函数数、类类和和名名字字空空间间之之外外说说明明的的变变量量的的作作用用域域从从被被说说明明点点开开始始,到到所所在在的的程程序序文文件件结结束束具具有有程程序序级级作作用用域域。它它们们被被分分配配在在静静态态数数据据区区之之中中。即即是是说说,它它们们是是作作用用域域最最大大且且“生生命命期期”最最长长的的一一种种变变量量。在在另另一一个个程程序序文文件件中中如如果果需需要要使使用用同同一一个个变变量量的的话话,必必须须把把这这个个变变量量说说明明为为外外部部(externextern)的的,表表示示这这个个变变量量的的说说明明不不在在本本程程序序文文件件中中,而

57、在原文件而在原文件之中。之中。59 (3) (3) 静态静态( (static)static)变量变量 又又可可分分为为局局部部静静态态变变量量和和全全局局静静态态变变量量。局局部部静静态态变变量量是是指指在在函函数数或或块块的的内内部部说说明明的的静静态态变变量量,它它的的作作用用域域是是局局部部的的;对对于于局局部部变变量量:增增加加staticstatic说说明明,使使其其生生存存期期扩扩展展到到整整个个程程序序。而而全全局局静静态态变变量量指指的的是是在在所所有有函函数数的的外外部部说说明明的的具具有有单单文文件件级级全全局局性性的的静静态态变变量量。静静态态全全局局变变量量的的生生存

58、存期期为为整整个个程程序序,作作用用域为本程序文件,不可扩展。域为本程序文件,不可扩展。 两两种种静静态态变变量量都都被被分分配配在在静静态态数数据据区区之之中中,即即是是说说,它它们们的的“生命期生命期”都与整个程序的执行期相同都与整个程序的执行期相同。 对对于于函函数数:一一般般(类类外外)函函数数的的生生存存期期和和作作用用域域为为整整个个程程序序,静静态态属属性性的的函函数数的的作作用用域域被被限限于于所所在在的的程程序序文文件件,这这时时,该该函函数不能在其它程序文件中使用。数不能在其它程序文件中使用。 60 函数的存储类别函数的存储类别 函函 数数 也也 分分 为为 两两 种种 存

59、存 储储 类类 别别 , 一一 种种 是是 外外 部部(externextern)存存储储类类别别,另另一一种种是是静静态态(staticstatic)存存储类别。储类别。 (1) (1) 外部外部( (extern)extern)函数函数 具具有有外外部部(externextern)存存储储类类别别的的函函数数称称为为外外部部(externextern)函函数数。这这种种函函数数具具有有程程序序级级作作用用域域。当当函函数数定定义义时时没没给给出出存存储储类类别别时时,系系统统默默认认它它为为外外部部(externextern)存存储储类类别别,所所以以实实用用程程序序中中几几乎乎从从不不使

60、使用用externextern来说明外部函数。来说明外部函数。 61 例如,如下的两个函数说明是完全等价的。例如,如下的两个函数说明是完全等价的。intint funcfunc()()externextern intint funcfunc()() (2) (2) 静态静态( (static)static)函数函数 具具有有静静态态(staticstatic)存存储储类类别别的的函函数数称称为为静静态态(staticstatic)函函数数(有有时时也也称称为为内内部部函函数数)。这这种种函函数数只只具具有有文文件件级级作作用用域域,即即是是说说, , 这这样样的的函函数数只只能能在本文件的内部

61、被调用,在其它文件中均不可见。在本文件的内部被调用,在其它文件中均不可见。62 存储类别相关实例存储类别相关实例 1. 1. 存储类别实例存储类别实例1 - 1 - 外部(外部(externextern)变量变量 本实例程序由两个文件构成本实例程序由两个文件构成, ,下面分别叙述它们。下面分别叙述它们。 (1)(1)程序文件程序文件1 1 本本文文件件中中对对外外部部变变量量x x及及chch进进行行定定义义,并并通通过过调调用用处处于于另另一一文文件件中中的的函函数数来来“传传递递”与与使使用用这这些些外外部变量的具体值。部变量的具体值。 63 /file_1.cppfile_1.cpp(源

62、程序文件源程序文件1 1,含有,含有2 2个函数)个函数)# #include include intint x=11; x=11;char char chch=A; =A; void func1();void func1();/函数原型函数原型void func2();void func2();/函数原型,具体定义在另一个文件中函数原型,具体定义在另一个文件中void main()void main() func1(); func1(); func2(); func2(); coutcoutIn file1_main: x, In file1_main: x, chch=x, =x, chc

63、hendlendl; ; void func1()void func1() coutcoutIn file1_func1: x, In file1_func1: x, chch=x, =x, chchendlendl; ; 64 (2)(2)程序文件程序文件2 2 本本文文件件通通过过externextern关关键键字字来来对对外外部部变变量量x x及及chch进进行行说说明明(不不再再分分配配存存储储空空间间,与与另另一一文文件件定定义义处处的的那那一一同同名名变变量量共共享享同同一一存存储储空空间间),并并通通过过使使用用与与修修改改这这些些外外部部变变量量的的值值来来达达到到文文件件间间

64、相相互互通通讯讯的的目目的。的。 65 /file_2.cppfile_2.cpp(源程序文件源程序文件2 2,含有,含有1 1个函数)个函数)# #include include extern extern intint x; x;extern char extern char chch; ; void func2()void func2() coutcoutIn file2_func2: x, In file2_func2: x, chch=x, =x, chchendlendl; ; x=22; x=22; chch=B; =B; 程序执行后的显示结果如下:程序执行后的显示结果如下:In

65、 file1_func1: x, In file1_func1: x, chch=11, A=11, AIn file2_func2: x, In file2_func2: x, chch=11, A=11, AIn file1_main: x, In file1_main: x, chch=22, B=22, B66 存储类别实例存储类别实例2 - 2 - 局部静态局部静态( (static)static)变量变量 局局部部静静态态(staticstatic)变变量量与与局局部部自自动动(autoauto)变变量量的的使使用用区区别别是是: : 它它们们的的作作用用域域都都是是局局部部的的,

66、作作用用域域外外都都是是不不可可见见的的;但但局局部部静静态态(staticstatic)变变量量在在其其作作用用域域外外仍仍是是存存在在的的,而而局局部部自自动动(autoauto)变变量在其作用域外则不复存在。量在其作用域外则不复存在。 67 #include void f1();void main() int i;for (i=1; i=3; i+)f1(); void f1() int a=1; /局部自动变量局部自动变量a a static int s=1; /局部静态变量局部静态变量s s coutIn f1 - pos1: a(auto)=a s(static)=sendl; a

67、+=2;s+=2; coutIn f1 - pos2: a(auto)=a s(static)=snn;68 程序执行后的显示结果如下:程序执行后的显示结果如下:In f1 - pos1: a(auto)=1 s(static)=1In f1 - pos1: a(auto)=1 s(static)=1In f1 - pos2: a(auto)=3 s(static)=3In f1 - pos2: a(auto)=3 s(static)=3In f1 - pos1: a(auto)=1 s(static)=3In f1 - pos1: a(auto)=1 s(static)=3In f1 - p

68、os2: a(auto)=3 s(static)=5In f1 - pos2: a(auto)=3 s(static)=5In f1 - pos1: a(auto)=1 s(static)=5In f1 - pos1: a(auto)=1 s(static)=5In f1 - pos2: a(auto)=3 s(static)=7In f1 - pos2: a(auto)=3 s(static)=769名字的生存期和作用域名字的生存期和作用域 名字(变量名、函数名等等)的生存期与其对应的语法实体被分配的存储名字(变量名、函数名等等)的生存期与其对应的语法实体被分配的存储空间相关全局的、静态的、

69、外部的语法实体被分配到全局数据区空间相关全局的、静态的、外部的语法实体被分配到全局数据区 ,生存生存期为整个程序期为整个程序 局部的(在函数内,程序块内说明的)语法实体被分配到局部数据区局部的(在函数内,程序块内说明的)语法实体被分配到局部数据区(栈区等)(栈区等) ,这种分配是临时的,一旦该函数体或程序块这些结束,所,这种分配是临时的,一旦该函数体或程序块这些结束,所分配的空间被撤销,分配的空间被撤销,局部名字的生存期从被说明开始,到程序块结束局部名字的生存期从被说明开始,到程序块结束。从这里也可看到,生存期与一个程序被划分为一个或多个程序文件是没从这里也可看到,生存期与一个程序被划分为一个

70、或多个程序文件是没有关系的。有关系的。 名字的作用域与程序文件有关,作用域的概念是从这个名字能够被引用的,名字的作用域与程序文件有关,作用域的概念是从这个名字能够被引用的,角度考虑的,一个名字在某处可以被引用,也称它在这里是可见的,它角度考虑的,一个名字在某处可以被引用,也称它在这里是可见的,它可被引用的范围,就是该名字的作用域。可被引用的范围,就是该名字的作用域。 简单说简单说作用域是指标识符(如变量名、参数名、函数名等)在程序正文中作用域是指标识符(如变量名、参数名、函数名等)在程序正文中的有效范围。的有效范围。 70 (1) (1) 程序级程序级作用域(也称作用域(也称多文件级多文件级作

71、用域)作用域) 属于程序级作用域的有通过属于程序级作用域的有通过externextern存储类别进行说明的外存储类别进行说明的外部变量以及外部函数等。部变量以及外部函数等。 (2) (2) 文件级文件级作用域(也称作用域(也称单文件级单文件级作用域)作用域) 其有效范围为定义该标识符的那一个其有效范围为定义该标识符的那一个文件内文件内。属于此种。属于此种作用域的有静态函数以及在各函数之外的某一位置进行说明作用域的有静态函数以及在各函数之外的某一位置进行说明的那些变量等。的那些变量等。 (3) (3) 类级类级作用域作用域 有效范围为所定义的那一个类的有效范围为所定义的那一个类的类体内类体内。类

72、中的私有成。类中的私有成员的作用域仅在其类体内,公有成员以及保护成员的作用域员的作用域仅在其类体内,公有成员以及保护成员的作用域有所不同。关于类级作用域将在后面的章节再进一步讨论。有所不同。关于类级作用域将在后面的章节再进一步讨论。71 (4) (4) 函数级函数级作用域作用域( (也称单函数级作用域也称单函数级作用域) ) 有有效效范范围围为为所所处处的的那那一一个个函函数数的的函函数数体体内内。属属于于此此种种作作用用域域的的有有函函数数的的形形参参、在在函函数数体体内内说明的变量、以及语句标号等。说明的变量、以及语句标号等。 (5) (5) 块级块级作用域作用域 块块是是程程序序正正文文

73、中中被被一一对对花花括括号号括括起起来来的的那那一一块块区区域域(如如, , 函函数数内内的的某某一一个个复复合合语语句句)。C+C+允许在块中说明局部于该块的变量。允许在块中说明局部于该块的变量。 72(6) (6) 函数原型级函数原型级作用域作用域 仅仅指指出出在在函函数数原原型型声声明明时时形形式式参参数数的的作作用用范范围围。例如,假设有如下的函数原型声明:例如,假设有如下的函数原型声明: double double myfunc(intmyfunc(int iparaipara);); 此此时时的的形形式式参参数数iparaipara的的有有效效范范围围( (作作用用域域) )仅仅局

74、局限限于于说说明明该该参参数数的的那那一一对对圆圆括括号号之之间间,在在程程序序的的其他任何地方都无法引用该标识符。其他任何地方都无法引用该标识符。73 关于重名标识符的作用域关于重名标识符的作用域 重重名名标标识识符符指指的的是是在在程程序序中中被被重重复复定定义义的的同同名名标标识识符符。在在相相同同的的作作用用域域内内,标标识识符符不不能能被被重重复复定定义义。但但在在不不同同的的作作用用域域内内,允允许许对对标标识识符符进进行行重重复复定义。定义。 重名标识符的作用域遵循如下的规则:重名标识符的作用域遵循如下的规则: (1) (1) 没有包含关系的两个不同作用域没有包含关系的两个不同作

75、用域 在在其其中中说说明明的的标标识识符符尽尽管管名名字字相相同同,但但二二者者毫毫不不相干。相干。 74 (2) (2) 具有包含关系的两个不同作用域具有包含关系的两个不同作用域 将将它它们们看看成成是是互互不不相相同同的的名名字字;进进入入子子范范围围后后, , 将将屏屏蔽蔽其其父父范范围围的的名名字字。即即是是说说,进进入入子子范范围围后后, , 原原父父范范围围处处定定义义的的那那一一同同名名标标识识符符将将是是不不可可见见的的,但但它它仍仍然然存存在在;当当退退出出了了子子范范围围后后,原原父父范范围围处处定定义的那一同名标识符将又成为可见的了。义的那一同名标识符将又成为可见的了。

76、75 与作用域有关的程序实例与作用域有关的程序实例 1. 1. 作用域实例作用域实例1 1 本本实实例例出出现现了了三三个个不不同同的的作作用用域域:函函数数级级作作用用域域,被被函函数数级级作作用用域域所所包包含含的的外外层层块块级级作作用用域域,被被外外层层块级作用域所包含的块级作用域所包含的内嵌块级内嵌块级作用域。作用域。 76 # #include include void main()void main() intint i=10; i=10;/整型变量整型变量i, i, 具有函数级作用域具有函数级作用域 char char chch=1;=1; coutcoutin main -

77、in main - i,chi,ch=i, =i, chchendlendl; ; intint i=20; i=20;/另一整型变量另一整型变量i, i, 外层块级作用域外层块级作用域char char chch=2;=2;coutcoutin local1 - in local1 - i,chi,ch=i, =i, chch0)if(i0) double i=30.3; / double i=30.3; /双精度变量双精度变量i,i,内嵌块级作用域内嵌块级作用域 intint chch=33;=33; coutcoutin local2 - in local2 - i,chi,ch=i,

78、=i, chchendlendl; ; coutcoutin local1 - in local1 - i,chi,ch=i, =i, chchendlendl; ; coutcoutin main - in main - i,chi,ch=i, =i, chchendlendl; ; 78 程序执行后的显示结果如下:程序执行后的显示结果如下:in main - in main - i,chi,ch=10, 1=10, 1in local1 - in local1 - i,chi,ch=20, 2=20, 2in local2 - in local2 - i,chi,ch=30.3, 33=3

79、0.3, 33in local1 - in local1 - i,chi,ch=20, 2=20, 2in main - in main - i,chi,ch=10, 1=10, 179 2. 2. 作用域实例作用域实例2 2 本本实实例例主主要要用用于于说说明明文文件件级级作作用用域域(全全局局变变量量),函函数数级级作作用用域域(局局部部变变量量),以以及及函函数数原原型型级级作作用用域域的的相相互互关关系系及及其其使使用用。其其中中还还出出现现了了两两个个具具有有“平行平行”关系的函数作用域。关系的函数作用域。 80 # #include include intint x=11; x=1

80、1; / x/ x具有文件级作用域具有文件级作用域char char chch=1; =1; void func1(int ipara1); /ipara1void func1(int ipara1); /ipara1仅具有函数原型级作用域仅具有函数原型级作用域void func2()void func2() intint i=22222; i=22222;/函数级函数级i i,与与func1func1中中i i重名但不相干重名但不相干 double double chch=202.2; =202.2; /函数级函数级chch,与文件级与文件级chch同名同名 coutcoutin func2

81、 - in func2 - x,chx,ch=x, =x, chchendlendl; ; coutcoutin func2 - i=iin func2 - i=iendlendl; ; 81 void main()void main()coutcoutin main - in main - x,chx,ch=x, =x, chchendlendl; ; func1(x);func1(x);func2();func2(); void func1(int ii) void func1(int ii) intint i=21111; i=21111; /函数级函数级i i,与与func2func2

82、中中i i重名但不相干重名但不相干 intint x=201; x=201; coutcoutin func1 - ii=iiin func1 - ii=iiendlendl; ;coutcoutin func1 - in func1 - x,chx,ch=x, =x, chchendlendl; ; coutcoutin func1 - i=iin func1 - i=iendlendl; ; 82 程序执行后的显示结果如下:程序执行后的显示结果如下:in main - in main - x,chx,ch=11, 1=11, 1in func1 - ii=11in func1 - ii=1

83、1in func1 - in func1 - x,chx,ch=201, 1=201, 1in func1 - i=21111in func1 - i=21111in func2 - in func2 - x,chx,ch=11, 202.2=11, 202.2in func2 - i=22222in func2 - i=22222835.5.6.1 6.1 “三色冰激凌三色冰激凌”程序程序 - 由由冰冰激激凌凌商商提提出出的的问问题题:有有2828种种颜颜色色的的原原料料,可可以以组组合合成成多多少少种种3 3色色冰冰激激凌凌。问问题题归归结结为为计计算算排排列数与组合数。列数与组合数。 本

84、本示示例例计计算算排排列列数数A(elements,selections)A(elements,selections)及及组合数组合数C(elements,selections)C(elements,selections)。 如如:A(3,2)=6, A(3,2)=6, C(3,2)=3; C(3,2)=3; A(28,3)=19656, A(28,3)=19656, C(28,3)=3276C(28,3)=3276。84 由于排列数由于排列数A(ele,selA(ele,sel) )与组合数与组合数C(ele,selC(ele,sel) )间有如间有如下的关系:下的关系:C(ele,selC

85、(ele,sel)=)=A(ele,sel)/selA(ele,sel)/sel! ! 程序中编制了一个求阶乘的递归函数程序中编制了一个求阶乘的递归函数factorialfactorial,可用于求出可用于求出selsel的阶乘。的阶乘。 使程序执行后的显示结果如下:使程序执行后的显示结果如下:Number of selections:Number of selections:3 3Out of how many elements:Out of how many elements:2828A(28,3)=19656A(28,3)=19656C(28,3)=3276C(28,3)=327685

86、# #include include long long factorial(intfactorial(int number); / number); /函数原型函数原型void main( )void main( )intint i,selections,elements; i,selections,elements; /计算计算A(elements,selections)A(elements,selections)/以及以及C(elements,selections)C(elements,selections)coutcoutNumber of selections:;selections

87、; /selections; /输入整数输入整数selectionsselectionscoutcoutOut of how many elements:;elements; /elements; /输入整数输入整数elementselementsdouble answer = elements;double answer = elements;intint eleele = elements; = elements; 86/求排列数求排列数A A:共进行共进行sel-1sel-1次乘法次乘法/A(ele,selA(ele,sel)=)=eleele*(ele-1)*.*(ele-sel+1)

88、 *(ele-1)*.*(ele-sel+1) for(i=1; iselections; i+) /for(i=1; iselections; i+) /循环循环sel-1sel-1次次 answer*=-answer*=-eleele; ; /最终的最终的answeranswer值即为所要求的值即为所要求的A(ele,selA(ele,sel) )coutcoutA(elements,selections)=;A(elements,selections)=;coutcoutansweranswerendlendl; ; /输出排列数输出排列数A(ele,selA(ele,sel) )之结果

89、之结果 87 /组合数组合数C C的求法:的求法:/ / C(ele,selC(ele,sel)=)=A(ele,sel)/selA(ele,sel)/sel! !answer/=factorial(selections); answer/=factorial(selections); /对递归函数调用,求阶乘对递归函数调用,求阶乘coutcoutC(elements,selections)=;C(elements,selections)=;coutcoutansweranswerendlendl; ; /输出组合数输出组合数C(ele,selC(ele,sel) )之结果之结果 88 lon

90、g long factorial(intfactorial(int number) number) /递归函数递归函数factorialfactorial,用于算出用于算出numbernumber的阶乘的阶乘 if(number=1) if(number=1) return 1; / return 1; /递归出口递归出口( (“退出退出”递归递归) )else else return number* return number*factorial(number-1)factorial(number-1); ; / /自递归调用自递归调用, , 注意实参不同注意实参不同( (每次降每次降1)1

91、) 89当然也可编出非递归的用于算出当然也可编出非递归的用于算出numbernumber之阶乘的函数之阶乘的函数factorialfactoriallong long factorial(intfactorial(int number) / number) /非递归函数非递归函数 long p=1;long p=1;for (for (intint i=2; i=number; i+) i=2; i pnumber = preturn p; return p; /返回的返回的p p即为即为numbernumber之阶乘之阶乘 905.6.2 Hanoi5.6.2 Hanoi塔问题塔问题 - 古

92、印度的著名智力测验问题:有三个立柱古印度的著名智力测验问题:有三个立柱A A、B B、C C,在在A A柱上穿有大小不等的圆盘柱上穿有大小不等的圆盘6464个,较大的圆个,较大的圆盘在下,较小者在上。要求借助于盘在下,较小者在上。要求借助于B B柱将柱将A A柱上的柱上的6464个圆盘移到个圆盘移到C C柱,规则为:柱,规则为: (1) (1) 每次只能把一个柱上最上面的圆盘移至另每次只能把一个柱上最上面的圆盘移至另一个柱的最上面一个柱的最上面; ; (2) (2) 每个柱上总保持较大的圆盘在下,较小者每个柱上总保持较大的圆盘在下,较小者在上。在上。 编制程序编制程序, , 实现将任意实现将任

93、意n n个圆盘从个圆盘从A A柱借助于柱借助于B B柱移到柱移到C C柱柱, , 并显示出全部移动过程。并显示出全部移动过程。91 总任务总任务(“(“度度”为为n n的任务的任务) ): 把把A A柱上的柱上的n n个圆盘,借助于个圆盘,借助于B B柱,按规则柱,按规则移到移到C C柱上柱上( (移动规则:一次移一片,大片不可移动规则:一次移一片,大片不可压小片压小片) ) 。 靠调用自定义函数靠调用自定义函数hanoihanoi来完成:来完成:hanoi(n,A,B,Chanoi(n,A,B,C););92 总任务可分解为与其等价的总任务可分解为与其等价的三个子任务三个子任务( (的的“合

94、合集集”)(“”)(“度度”小于等于小于等于n-1n-1的三个子任务的三个子任务) ): (1)(1) 把把A A柱上最上面柱上最上面n-1n-1个圆盘个圆盘,借助于,借助于C C柱,按柱,按规则移到规则移到B B柱上柱上( (一次递归调用一次递归调用) ); (2)(2) 把把A A柱上留下的柱上留下的( (最大的最大的) )圆盘圆盘移到移到C C柱上柱上( (一一步可完成的步可完成的“本原任务本原任务”)”); (3)(3) 把把B B柱上的柱上的n-1n-1个圆盘个圆盘,借助于,借助于A A柱,按规则柱,按规则移到移到C C柱上柱上( (又一次递归调用又一次递归调用) )。 93 靠下面

95、的三个调用语句来完成:靠下面的三个调用语句来完成:hanoi(n-1,A,C,B);hanoi(n-1,A,C,B);move(A,C);move(A,C);hanoi(n-1,B,A,C);hanoi(n-1,B,A,C);94移动次序移动次序:1 1A B C95移动次序移动次序:2 2A B C96移动次序移动次序:3 3A B C97移动次序移动次序:4 4A B C98移动次序移动次序:5 5A B C99移动次序移动次序:6 6A B C100移动次序移动次序:7 7A B C101移动次序移动次序:8 8A B C102使程序执行后的显示结果如下:使程序执行后的显示结果如下:In

96、put the number of disks:Input the number of disks:3 3The step of moving 3 disks:The step of moving 3 disks:A=CA=CA=BA=BC=BC=BA=CA=CB=AB=AB=CB=CA=CA=C103具体程序如下:具体程序如下:# #include include void void hanoi(inthanoi(int, char, char, char); /, char, char, char); /hanoihanoi函数原型函数原型void main( ) void main( )

97、 intint m; m; /欲移动的圆盘个数欲移动的圆盘个数m mcoutcoutInput the number of disks:;m; m; /输入圆盘个数输入圆盘个数m mcoutcoutendlendlThe step of moving m disks:;The step of moving m disks:;hanoi(m,A,B,Chanoi(m,A,B,C); ); / /调用自定义函数调用自定义函数hanoihanoi,移移m m片,从片,从A A,借助借助B B,到到C Ccoutcoutendlendl; ; 104 void move (char from, cha

98、r to) void move (char from, char to) /移移1 1片,从片,从fromfrom,到到to (“to (“本原任务本原任务”)”)coutcoutendlendlfromto; fromto; /显示显示 移步信息移步信息 105void void hanoi(inthanoi(int n, char a, char b, char c) n, char a, char b, char c)/自递归函数自递归函数hanoihanoi,移移n n片,从片,从a a,借助借助b b,到到c cif(n=1) if(n=1) move(a,c); /“move(a,c); /“本原任务本原任务”,递归出口,递归出口else else hanoi(n-1,a,c,b); hanoi(n-1,a,c,b); / /移移n-1n-1片,从片,从a a,借助借助c c,到到b (b (递归调用递归调用) )move(a,c);move(a,c); / /移移1 1片,从片,从a a,到到c (“c (“本原任务本原任务”)”)hanoi(n-1,b,a,c); hanoi(n-1,b,a,c); / /移移n-1n-1片,从片,从b b,借助借助a a,到到c (c (递归调用递归调用) ) 106

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 高等教育 > 研究生课件

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号