函数的设计与应用.ppt
第第5 5章章 函数的设计与应用函数的设计与应用5.1 5.1 5.2 5.2 5.3 5.3 5.4 5.4 5.5 5.5 函数的递归函数的递归5.6 5.6 内联函数内联函数5.7 5.7 数组参数及函数间传递数据的五种常用方式数组参数及函数间传递数据的五种常用方式(渠道渠道)5.8 5.8 标识符的作用域标识符的作用域5.9 5.9 变量与函数的存储类别变量与函数的存储类别5.10 5.10 编译预处理编译预处理1问题问题1 1,为什么要用函数,为什么要用函数2 2,使用函数的程序和顺序程序有什么,使用函数的程序和顺序程序有什么区别?区别?课本课本p116p1162 函数调用的堆栈函数调用的堆栈 堆栈堆栈Main()cuberoot(x)参数传递、返回值参数传递、返回值保护现场、恢复现场保护现场、恢复现场调用调用返回返回35.5 5.5 函数的递归函数的递归5.5.1 5.5.1 使用递归求累乘积与累加和使用递归求累乘积与累加和5.5.2“5.5.2“三色冰激凌三色冰激凌”程序程序 5.5.3 5.5.3 HanoiHanoi塔问题塔问题4 C+C+允允许许函函数数自自己己调调用用自自己己(如如A A函函数数可可以以调调用用A A函函数数本本身身,称称为为直直接接递递归归)。也也允允许许A A函函数数调调用用B B函函数数,而而后后B B函函数数又又调调用用A A函函数数(从从而而形形成成间间接接递递归归)。但但不不论论使使用用哪哪种种递递归归,程程序序员员都都应应保保障障递递归归函函数数在在执执行行若若干干次次后后能能够够“退退出出”递递归归(不再进行递归调用,也即能够实现递归出口不再进行递归调用,也即能够实现递归出口)。55.5.1 5.5.1 使用递归求累乘积与累加和使用递归求累乘积与累加和 1.1.求求n n的阶乘的递归函数的阶乘的递归函数prodprod -参看书p124的5.2.6小节的2使程序执行后的输出结果为使程序执行后的输出结果为:Input a positive integer:Input a positive integer:6 6p=1*.*6=720p=1*.*6=720在数学中,可按如下方式对在数学中,可按如下方式对n!n!进行递归定义:进行递归定义:当当n=1n=1时,时,n!=1n!=1;当当n1n1时,时,n!=n*(n-1)!n!=n*(n-1)!这正是我们编写求这正是我们编写求n n的阶乘的递归函数的阶乘的递归函数prodprod的基础。的基础。6#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 n乘以乘以n-1n-1的阶乘的阶乘/(/(使用自递归调用使用自递归调用“prod(n-1)prod(n-1)”来求出来求出(n-1)!)n-1)!)7void 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 8 上上述述求求阶阶乘乘的的递递归归函函数数中中,当当主主函函数数通通过过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),此此时时系系统统将将再再一一次次对对prodprod本本身身进进行行递递归归调用,但此时的实参又调用,但此时的实参又“下降下降”了了1 1。9 正正是是通通过过这这种种实实参参的的逐逐次次“下下降降”,可可保保障障递递归归函函数数在在执执行行若若干干次次后后(此此时时的的求求阶阶乘乘问问题题当当“下下降降”到到1 1时时),能能够够“退退出出”递递归归(不不再再进进行行递递归归调调用,也即实现了递归出口用,也即实现了递归出口)。由由于于prod(1)prod(1)的的返返回回值值为为1 1,系系统统进进一一步步算算出出2*2*prod(1)prod(1)的的值值(即即prod(2)prod(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)。10 虽虽然然上上述述执执行行过过程程是是由由系系统统自自动动完完成成的的,但但程程序序员员要要理理解解并并熟熟知知这这种种调调用用机机制制与与系系统统实实现现方方法法,从从而而才才能能编编写写出出逻逻辑辑正正确确且且简简明明易易懂懂的的递递归归处处理理程程序。序。另另外外注注意意,递递归归处处理理程程序序的的执执行行速速度度通通常常要要比比非递归处理方法慢。非递归处理方法慢。问题:为什么会慢?问题:为什么会慢?11 2.2.求出求出1 1到到n n之累加和的递归函数之累加和的递归函数sumsum 使程序执行后的输出结果为使程序执行后的输出结果为:Input a positive integer:Input a positive integer:100100s=1+.+100=5050s=1+.+100=505012#include include intint sum(intsum(int n)n)/递归函数递归函数sumsum if(n=1)if(n=1)/n/n等于等于1 1时,递归出口时,递归出口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个个数数的的累累加加和和)13 void main()void main()intint n;n;coutcoutInput a positive integer:;n;n;intint s=sum(n);/s=sum(n);/求出从求出从1 1累加到累加到n n的和放的和放s s中中 coutcouts=1+.+n=ss=1+.+n=sendlendl;145.5.2 5.5.2“三色冰激凌三色冰激凌”程序程序 -参看书参看书p130p130的的5.4.15.4.1小节小节 由由冰冰激激凌凌商商提提出出的的问问题题:有有2828种种颜颜色色的的原原料料,可可以以组组合合成成多多少少种种3 3色色冰冰激激凌凌。问问题题归归结结为为计计算算排排列数与组合数。列数与组合数。本本示示例例计计算算排排列列数数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。15 由于排列数由于排列数A(ele,selA(ele,sel)与组合数与组合数C(ele,selC(ele,sel)间有如间有如下的关系:下的关系:C(ele,selC(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)=327616#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;/selections;/输入整数输入整数selectionsselectionscoutcoutOut of how many elements:;elements;/elements;/输入整数输入整数elementselementsdouble answer=elements;double answer=elements;intint eleele=elements;=elements;17/求排列数求排列数A A:共进行共进行sel-1sel-1次乘法次乘法/A(ele,selA(ele,sel)=)=eleele*(ele-1)*.*(ele-sel+1)*(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)之结果之结果 18 /组合数组合数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)之结果之结果 19 long 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)20当然也可编出非递归的用于算出当然也可编出非递归的用于算出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之阶乘之阶乘 215.5.3 Hanoi5.5.3 Hanoi塔问题塔问题 -参看书p132的5.4.2小节 古印度的著名智力测验问题:有三个立柱古印度的著名智力测验问题:有三个立柱A A、B B、C C,在在A A柱上穿有大小不等的圆盘柱上穿有大小不等的圆盘6464个,较大的圆个,较大的圆盘在下,较小者在上。要求借助于盘在下,较小者在上。要求借助于B B柱将柱将A A柱上的柱上的6464个圆盘移到个圆盘移到C C柱,规则为:柱,规则为:(1)(1)每次只能把一个柱上最上面的圆盘移至另每次只能把一个柱上最上面的圆盘移至另一个柱的最上面一个柱的最上面;(2)(2)每个柱上总保持较大的圆盘在下,较小者每个柱上总保持较大的圆盘在下,较小者在上。在上。编制程序编制程序,实现将任意实现将任意n n个圆盘从个圆盘从A A柱借助于柱借助于B B柱移到柱移到C C柱柱,并显示出全部移动过程。并显示出全部移动过程。22 总任务总任务(“(“度度”为为n n的任务的任务):把把A A柱上的柱上的n n个圆盘,借助于个圆盘,借助于B B柱,按规则柱,按规则移到移到C C柱上柱上(移动规则:一次移一片,大片不可移动规则:一次移一片,大片不可压小片压小片)。靠调用自定义函数靠调用自定义函数hanoihanoi来完成:来完成:hanoi(n,A,B,Chanoi(n,A,B,C););23 总任务可分解为与其等价的总任务可分解为与其等价的三个子任务三个子任务(的的“合合集集”)(“”)(“度度”小于等于小于等于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柱上柱上(又一次递归调用又一次递归调用)。24 靠下面的三个调用语句来完成:靠下面的三个调用语句来完成: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);25移动次序移动次序:1 1A B C26移动次序移动次序:2 2A B C27移动次序移动次序:3 3A B C28移动次序移动次序:4 4A B C29移动次序移动次序:5 5A B C30移动次序移动次序:6 6A B C31移动次序移动次序:7 7A B C32移动次序移动次序:8 8A B C33使程序执行后的显示结果如下:使程序执行后的显示结果如下:Input 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=C34具体程序如下:具体程序如下:#include include void void hanoi(inthanoi(int,char,char,char);/,char,char,char);/hanoihanoi函数原型函数原型void main()void main()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;35 void move(char from,char to)void move(char from,char to)/移移1 1片,从片,从fromfrom,到到to(“to(“本原任务本原任务”)”)coutcoutendlendlfromto;fromto;/显示显示 移步信息移步信息 36void 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(递归调用递归调用)375.6 5.6 内联函数内联函数 -参看书参看书p125p125的的5.2.75.2.7小节小节 可在一般的函数说明前冠以关键字可在一般的函数说明前冠以关键字inlineinline,称这样的函数为称这样的函数为内联函数内联函数。按如下的方式来说。按如下的方式来说明:明:inline inline ()38 在编译过程中,凡内联函数,在编译过程中,凡内联函数,系统均把它的系统均把它的执行代码插入到该函数的每个调用点处执行代码插入到该函数的每个调用点处(以取以取代那一函数调用代那一函数调用),从而使程序执行过程中,从而使程序执行过程中,每次对该函数调用时不需控制转移,可每次对该函数调用时不需控制转移,可节省节省执行时间执行时间;但由于每个调用点处均出现那一但由于每个调用点处均出现那一函数的执行代码拷贝,相对来说使用内联函函数的执行代码拷贝,相对来说使用内联函数后数后会扩大其代码空间会扩大其代码空间。39使用内联函数的简单实例使用内联函数的简单实例#include include inline inline intint max(intmax(int x,x,intint y)/y)/内联函数内联函数maxmax return(xy?x:y);return(xy?x:y);40 void main(void)void main(void)intint a,b;a,b;coutcoutInput a,b:;ab;ab;coutcoutmax(a,b)=max(a,b)max(a,b)=max(a,b)endlendl;/对内联函数对内联函数maxmax的调用的调用 41 程序执行后的显示结果如下:程序执行后的显示结果如下:Input a,b:123 456Input a,b:123 456max(a,b)=456max(a,b)=456 使用内联函数时应注意:使用内联函数时应注意:1.1.内联函数的函数体一般讲不宜过大内联函数的函数体一般讲不宜过大,以以1-51-5行为宜。行为宜。2.2.凡在类体中定义的成员函数凡在类体中定义的成员函数(见第见第7 7章章)均均隐含为内联函数。隐含为内联函数。42 5.7 5.7 数组参数及函数间传递数据的五种数组参数及函数间传递数据的五种常用方式(渠道)常用方式(渠道)1.1.数组参数数组参数 数组可作为函数参数,从而把主调函数中数组可作为函数参数,从而把主调函数中的整个数组的整个数组(实际上是数组的首地址实际上是数组的首地址)传给了被调传给了被调函数。函数。而后可在被调函数中而后可在被调函数中使用使用或或改变改变传来的那传来的那些数组元素的值些数组元素的值。但注意,若在被调函数内改变。但注意,若在被调函数内改变数组元素值的话,返回主调函数后,相应实参数数组元素值的话,返回主调函数后,相应实参数组元素的值也进行了相同的改变(组元素的值也进行了相同的改变(“双向传值双向传值”功能)。功能)。43例例1.1.读如下程序,看执行读如下程序,看执行后会显示出什么结果后会显示出什么结果?#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 calling,ai=-n;for(intfor(int i=0;i10;i+)i=0;i10;i+)coutcoutAi ;Ai ;coutcoutendlendl;44 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;45 void void ProcessingFunc(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=tmptmp;b1+=100;b1+=100;46程序执行后的显示结果如下:程序执行后的显示结果如下:-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 101 2 3 4 9 8 7 66 047例例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=13648#include#include intint sum(intsum(int a,a,intint n)n)if(n=1)if(n=1)return a0;return a0;elseelse return(an-1+return(an-1+sum(a,n-1)sum(a,n-1););49 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=s“”=“下层下层”,“下层下层”=“”=“上层上层”。51 2)2)通过引用参数通过引用参数(“(“双向传递双向传递”方式,方式,有关引用的其它使用方法想。详见第有关引用的其它使用方法想。详见第6 6章章)传值方向可为:传值方向可为:“上层上层”=“”=“下层下层”,“下下层层”=“”=“上层上层”。即是说,它不仅可向被调函数。即是说,它不仅可向被调函数的形参的形参“传入传入”值值(调用时的实参值调用时的实参值),而且还可,而且还可通过该形参通过该形参“传出传出”值。系统处理方式为值。系统处理方式为:被调被调函数中对形参值的使用与改变,就是对主调函数函数中对形参值的使用与改变,就是对主调函数中调用语句处所对应实参变量值的直接使用与改中调用语句处所对应实参变量值的直接使用与改变变(形参不具有自己的局部于被调函数的存储空间,形参不具有自己的局部于被调函数的存储空间,它只是实参变量的一个它只是实参变量的一个“替身替身”)”)。52 3)3)通过数组参数或指针参数通过数组参数或指针参数(“(“双向传递双向传递”方式,指针参数的具体使用方法见第方式,指针参数的具体使用方法见第6 6章章)传值方向可为:传值方向可为:“上层上层”=“”=“下层下层”,“下层下层”=“”=“上层上层”。数组作形参,且数组作形参,且在被调函数内使用或改变在被调函数内使用或改变数组元素数组元素的值的值。系统处理方式为。系统处理方式为:对形参数组对形参数组元素的使用与改变,就是对实参数组元素的直元素的使用与改变,就是对实参数组元素的直接使用与改变。接使用与改变。指针作形参,且指针作形参,且在被调函数内使用或改变在被调函数内使用或改变指针所指变量指针所指变量的值的值。系统处理方式为。系统处理方式为:被调函被调函数中对形参指针所指变量值的使用与改变,就数中对形参指针所指变量值的使用与改变,就是对实参指针所指变量值的直接使用与改变。是对实参指针所指变量值的直接使用与改变。534)4)通过赋值参数通过赋值参数(“(“单向传递单向传递”方式方式)传值方向只是:传值方向只是:“上层上层”=“”=“下层下层”。也即,。也即,可从主调函数可从主调函数A A中通过赋值参数所对应的实参将值中通过赋值参数所对应的实参将值“传入传入”到被调函数到被调函数B B内内(去使用去使用),但不可将被调函数,但不可将被调函数B B内改变后的参数值内改变后的参数值“传出传出”到主调函数到主调函数A A中中(去接着去接着使用使用)。系统处理方式为。系统处理方式为:被调函数中对形参值的改被调函数中对形参值的改变不影响主调函数处的任一变量的值变不影响主调函数处的任一变量的值(形参分配有自形参分配有自己的局部于被调函数的存储空间,调用入口处将实己的局部于被调函数的存储空间,调用入口处将实参表达式的值赋给该局部变量参表达式的值赋给该局部变量)。54 5)5)通过函数返回值通过函数返回值(“(“单向传递单向传递”方方式式)通过函数内使用的通过函数内使用的returnreturn语句语句,可将被调函数可将被调函数B B内计算出的最终值内计算出的最终值“传传出出”到主调函数到主调函数A A的的“调用点调用点”处处(去去接着使用接着使用)。也即,传值方向只是:。也即,传值方向只是:“下层下层”=“”=“上层上层”。55 5.8 5.8 标识符的作用域标识符的作用域 5.8.1 5.8.1 作用域问题作用域问题 5.8.2 5.8.2 与作用域有关的程序实例与作用域有关的程序实例565.8.1 5.8.1 作用域问题作用域问题 作用域是指标识符(如变量名、参数名、函数名作用域是指标识符(如变量名、参数名、函数名等)在程序正文中的有效范围。等)在程序正文中的有效范围。1.1.六种不同级别的作用域六种不同级别的作用域 (1)(1)程序级程序级作用域(也称作用域(也称多文件级多文件级作用域)作用域)属于程序级作用域的有通过属于程序级作用域的有通过externextern存储类别进行存储类别进行说明的外部变量以及外部函数等。说明的外部变量以及外部函数等。57 (2)(2)文件级文件级作用域(也称作用域(也称单文件级单文件级作用域)作用域)其有效范围为定义该标识符的那一个其有效范围为定义该标识符的那一个文件内文件内。属于此种作用域的有静态函数以及在各函数之外的属于此种作用域的有静态函数以及在各函数之外的某一位置进行说明的那些变量等。某一位置进行说明的那些变量等。(3)(3)类级类级作用域作用域 有效范围为所定义的那一个类的有效范围为所定义的那一个类的类体内类体内。类中。类中的私有成员的作用域仅在其类体内,公有成员以及的私有成员的作用域仅在其类体内,公有成员以及保护成员的作用域有所不同。关于类级作用域将在保护成员的作用域有所不同。关于类级作用域将在后面的章节再进一步讨论。后面的章节再进一步讨论。58 (4)(4)函数级函数级作用域作用域(也称单函数级作用域也称单函数级作用域)有有效效范范围围为为所所处处的的那那一一个个函函数数的的函函数数体体内内。属属于于此此种种作作用用域域的的有有函函数数的的形形参参、在在函函数数体体内内说明的变量、以及语句标号等。说明的变量、以及语句标号等。(5)(5)块级块级作用域作用域 块块是是程程序序正正文文中中被被一一对对花花括括号号括括起起来来的的那那一一块块区区域域(如如,函函数数内内的的某某一一个个复复合合语语句句)。C+C+允许在块中说明局部于该块的变量。允许在块中说明局部于该块的变量。59(6)(6)函数原型级函数原型级作用域作用域 仅仅指指出出在在函函数数原原型型声声明明时时形形式式参参数数的的作作用用范范围围。例如,假设有如下的函数原型声明:例如,假设有如下的函数原型声明:double double myfunc(intmyfunc(int iparaipara););此此时时的的形形式式参参数数iparaipara的的有有效效范范围围(作作用用域域)仅仅局局限限于于说说明明该该参参数数的的那那一一对对圆圆括括号号之之间间,在在程程序序的的其他任何地方都无法引用该标识符。其他任何地方都无法引用该标识符。60 2.2.关于重名标识符的作用域关于重名标识符的作用域 重重名名标标识识符符指指的的是是在在程程序序中中被被重重复复定定义义的的同同名名标标识识符符。在在相相同同的的作作用用域域内内,标标识识符符不不能能被被重重复复定定义义。但但在在不不同同的的作作用用域域内内,允允许许对对标标识识符符进进行行重重复复定义。定义。重名标识符的作用域遵循如下的规则:重名标识符的作用域遵循如下的规则:(1)(1)没有包含关系的两个不同作用域没有包含关系的两个不同作用域 在在其其中中说说明明的的标标识识符符尽尽管管名名字字相相同同,但但二二者者毫毫不不相干。相干。61 (2)(2)具有包含关系的两个不同作用域具有包含关系的两个不同作用域 将将它它们们看看成成是是互互不不相相同同的的名名字字;进进入入子子范范围围后后,将将屏屏蔽蔽其其父父范范围围的的名名字字。即即是是说说,进进入入子子范范围围后后,原原父父范范围围处处定定义义的的那那一一同同名名标标识识符符将将是是不不可可见见的的,但但它它仍仍然然存存在在;当当退退出出了了子子范范围围后后,原原父父范范围围处处定定义的那一同名标识符将又成为可见的了。义的那一同名标识符将又成为可见的了。62 5.8.2 5.8.2 与作用域有关的程序实例与作用域有关的程序实例 1.1.作用域实例作用域实例1 1 本本实实例例出出现现了了三三个个不不同同的的作作用用域域:函函数数级级作作用用域域,被被函函数数级级作作用用域域所所包包含含的的外外层层块块级级作作用用域域,被被外外层层块级作用域所包含的块级作用域所包含的内嵌块级内嵌块级作用域。作用域。63#include include void main()void main()intint i=10;i=10;/整型变量整型变量i,i,具有函数级作用域具有函数级作用域 char char chch=1;=1;coutcoutin main-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,=i,chchendlendl;coutcoutin local1-in local1-i,chi,ch=i,=i,chchendlendl;coutcoutin main-in main-i,chi,ch=i,=i,chchendlendl;65 程序执行后的显示结果如下:程序执行后的显示结果如下: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=30.3,33in local1-in local1-i,chi,ch=20,2=20,2in main-in main-i,chi,ch=10,1=10,166 2.2.作用域实例作用域实例2 2 本本实实例例主主要要用用于于说说明明文文件件级级作作用用域域(全全局局变变量量),函函数数级级作作用用域域(局局部部变变量量),以以及及函函数数原原型型级级作作用用域域的的相相互互关关系系及及其其使使用用。其其中中还还出出现现了了两两个个具具有有“平行平行”关系的函数作用域。关系的函数作用域。67#include include intint x=11;x=11;/x/x具有文件级作用域具有文件级作用域char char chch=1;=1;void func1(int ipara1);/ipara1void fun