1、第七章 函 数 调 用,#include void main() int max_4(int a,int b,int c,int d); int a,b,c,d,max;printf(“Please enter 4 interger numbers:“);scanf(“%d %d %d %d“, ,7.6 函数的递归调用,或a(x)b() ;b()a(y) ; /* 间接的递归调用 */,C语言中,允许函数直接或间接地调用自己,这种调用方式称为函数的递归调用。C语言的特点之一就在于它允许函数的递归调用。其一般形式为:,a(x)a(y) ; /* 直接的递归调用 */,int age(int n
2、) int c; if(n=1)c=10;else c=age(n-1)+2;return(c); #include void main() printf(“%dn“,age(5); ,例7.6 有5个人坐在一起,第5个从比第4个人大2岁,第4个比第3个人大2岁, , 第1个人是10岁。问第5个人多大?,算法分两个阶段;第一阶段为“回溯”第二阶段为“递推”。,不应出现无终止的递归调用,因此,应该给定一个限制递归次数的条件。,递归调用对应的一般算法:,#include void main()long fac(int n); int n;long y;printf(“input an intege
3、r number:“);scanf(“%d“, ,例7.7 用递归方法求n!。求n!可以用递推算法,即从1开始,乘2,再乘3, 一直乘到n。这种方法容易理解,也容易实现。递推法的特点是从一个已知的事实出发,按一定的规律推出下一个事实,再从这新的已知事实出发,再向下推出一个新的事实。另一种方法是递归算法,即5!=4!X5, 4!=3!X4, ,1!=1 。,long fac(int n)long f;if(n0)printf(“n0,data error!“);else if(n=0|n=1) f=1;else f=fac(n-1)*n;return(f);,例7.8 Hanoi(汉诺)塔问题。
4、古代有一个梵塔,塔内有3个座A,B,C,开始时A座上有64个大小不等的盘子,大的在下小的在上。有一个老和尚想把这64个盘子从A座移到C座,但每次只允许移动一个盘子,且在移动的过程中在3个座上都始终保持大盘在下,小盘在上。在移动过程中可以利用B座。,为实现以上目标,老和尚:1)、命令第2个和尚将63个盘子从A座移到B座。2)、自己将1个盘(最底下的、最大的盘子)从A座移到C座。3)、再命令第2个和尚将63个盘子从B座移到C座。,为解决将63个盘子从A座移到B座,第2个和尚:1)、命令第3个和尚将62个盘子从A座移到C座。2)、自己将1个盘子从A座移到B座。3)、再命令第2个和尚将62个盘子从C座
5、移到B座。,如此层层下放,到第63个和尚,让他完成将2个盘子从一个座移到另一个座。第64个各尚将1个盘子从一个座移到另一个座。,为便于理解,先分析将A座上3个盘子移到C座上的过程:1、将A座上的2个盘子移到B座上(借助C)。2、将A座上1个盘子移到C座上。3、将B座上2个盘子移到C座上(借助A)。,其中第2步可以直接实现,第1步又可以用递归方法分解为:1)、将A上1个盘子从A座移到C座。2)、将A上1个盘子从A座移到B座。3)、将C上1个盘子从C座移到B座。,综合以上,可得移动3个盘子,经历7步,步骤为:AC,AB,CB,AC,BA,BC,AC,其中第3步又可以分解为:1)、将B上1个盘子从B
6、座移到A座。2)、将B上1个盘子从B座移到C座。3)、将A上1个盘子从A座移到C座。,综合以上分析,将n个盘子从A座移动到C座可以分解为以下3步:1、将A座上n-1个盘子借助C座先移到B座上。2、将A座上剩下的1个盘子移到C座上。3、将n-1个盘子从B座借助A座移到C座上。,上面的第1步和第3步,都是把n-1个盘子从一个座移到另一个座,采用的办法是一样的,只是座名不同而已。,因此,可以把上面的3个步骤,归成两类操作:1)、将n-1 个盘子从一个座移到另一个座上(n1)。2)、将1个盘子从一个座移到另一个座上。,移动n个盘子要经历2n-1步,移动64个盘子,要经历264-1步。,#include
7、 void main() void hanoi(int n,char one,char two,char three); int m;printf(“input the number of diskes:“);scanf(“%d“,void hanoi(int n,char one,char two,char three)void move(char x,char y);if(n=1) move(one,three);elsehanoi(n-1,one,three,two); /*从A到B借助C*/move(one,three); /*从A到C*/hanoi(n-1,two,one,three
8、); /*从B到C借助A*/ void move(char x,char y)printf(“%c%cn“,x,y); ,因此,可以把上面的3个步骤,归成两类操作:1)、将n-1 个盘子从一个座移到另一个座上(n1)。由hanoi()完成2)、将1个盘子从一个座移到另一个座上。由move()完成。,7.7 数组作为函数参数,#include void main()int large(int x,int y); int a10,b10,i,n=0,m=0,k=0;printf(“enter array a:n“);for(i=0;i10;i+)scanf(“%d“,7.7.1 数组元素作函数实参
9、,例7.9 有两个数组a和b,各有10个元素。n,m,k分别累计a数组元素大于、等于、小于b数组元素的个数。,for(i=0;i10;i+)if(large(ai,bi)=1)n+;else if(large(ai,bi)=0) m+;elsek+;,large(int x,int y)int flag;if(xy)flag=1;else if(xy)flag=-1;else flag=0;return(flag); ,printf(“aibi %d timesnai=bi %d timesnaik) printf(“array a is larger than array bn“);else
10、if (nk) printf(“array a is smaller than array bn“);elseprintf(“array a is equal to array bn“);,#include void main() float average(float array10); float score10,aver;int i;printf(“input 10 scores:n“);for(i=0;i10;i+)scanf(“%f“, ,7.7.2 数组名作函数实参,例7.10 求10个学生成绩的平均成绩。,float average(float array10)int i;flo
11、at aver,sum=array0;for(i=1;i10;i+)sum=sum+arrayi;aver=sum/10;return(aver); ,可以用数组名作函数参数,只是将数组的首元素的地址传递给所对应的形参,因此形参应当是数组名或指针变量。(传址),说明:1、用数组名作函数参数,应在主调函数和被调函数分别定义数组。不能只定义一方。,2、实参数组与形参数组类型应一致。,3、编译系统对形参数组大小不做检查,只是将实参数组的首元素的地址传给形参数组。,4、在被调函数中,声明形参数组时,可以不指定大小,在定义数组时可以在数组名后跟一个空的方括号。,#include void main()
12、float average(float array ,int n);float score_15=98.5,97,91.5,60,55;float score_210=67.5,89.5,99,69.5,77,89.5,76.5,54,60,99.5;printf(“The average of class A is %6.2fn“,average(score_1,5);printf(“The average of class B is %6.2fn“,average(score_2,10);,例7.11 分别求10个、5个学生成绩的平均成绩。,float average(float arra
13、y ,int n)int i;float aver,sum=array0;for(i=1;in;i+)sum=sum+arrayi;aver=sum/n;return(aver); ,#include void main() void sort(int array,int n);int a10,i;printf(“enter array:n“);for(i=0;i10;i+)scanf(“%d“, ,例7.12 用选择法对数组按由小到大排序。,void sort(int array,int n)int i,j,k,t;for(i=0;in-1;i+)k=i;for(j=i+1;jn;j+)if
14、(arrayjarrayk)k=j; t=arrayk;arrayk=arrayi;arrayi=t;,#include main() int max_value(int array4); int a34=1,3,5,7,2,4,6,8,15,17,34,12;printf(“Max value is %dn“,max_value(a); ,7.7.3 用多维数组名作函数参数,可以用多维数组名作为函数的实参和形参。在被调用函数中对形参数组定义时可以指定每一维的大小,也可以省略第一维的大小说明。但不能省略第二维以及其它高维的大小说明。在第二维大小相同的前提下,形参数组的第一维可以与实参数组不同。
15、,int max_value(int array4) int i,j,max;max=array00;for(i=0;imax) max=arrayij;return (max); ,例7.13 求34的二维数组所有元素中的最大值。,在第二维大小相同的前提下,形参数组的第一维可以与实参数组不同。例如:实参数组定义为:int score 510; 而形参数组定义为:int array 310; 或 int array 810;,7.8 变量的作用域-局部变量和全局变量,7.8.1 局部变量,当一个程序中包含多个函数时,在A函数中定义的变量在B函数中能否使用?这就是数据的作用域问题。,在一个函数内
16、部定义的变量只在本函数内有效,是局部变量。,例如: main() int x,y; /* x和y是局部变量,在main函数内有效 */int i,j; /* i和j是局部变量,在复合语句中有效 */ fun(int a,int b) /* 形参a、b是局部变量,在fun函数内有效 */ int m,n; /* m、n是局部变量,在fun函数内有效 */ ,说明1、在主函数中定义的变量,也只在主函数中有效,主函数也不能使用在其它函数中定义的变量。,2、在不同的函数中可以使用相同名字的变量,它们代表不同的对象,互不干扰。,3、形参也是局部变量,其它函数可以调用fun函数,但不引用fun函数的形参a
17、。,4、在一个函数内部,可以在复合语句中定义变量,这些变量只在本复合语句内有效。例如,下面主函数中的复合语句中定义的变量I,j。这种复合语句也称为“分程序”或“程序块”。,例如: main() int x,y; /* x和y是局部变量,在main函数内有效 */int i,j; /* i和j是局部变量,在复合语句中有效 */,7.8.1 全局变量,C语言,在进行编译时,编译系统是以源程序文件作为编译对象,或者说C的编译单位是源程序文件。在函数内部定义的变量是局部变量。而在函数外定义的变量是外部变量,也称为全局变量,它可以为本文件中其它函数所共用。它的有效范围是从定义变量的位置开始,到本源文件结
18、束。,说明1、设置全局变量的作用是增加函数间数据联系的渠道。在C程序设计中习惯将全局变量名的第一个字母大写。,#include float Max=0,Min=0; void main() float average(float array ,int n);float ave,score10;int i;for(i=0;i10;i+)scanf(“%f“,例7.14 有一个一维数组,内放10个学生的成绩,要求写一个函数,求出平均分、最高分、最低分。显然希望通过函数调用得到3个结果。除了可以从函数调用得到一个函数值,还可以利用全局变量返回二个值。,上例中,形参array和n的值由main函数调用
19、averge函数时,通过实参传递,函数中的返回值aver通过return语句返回。Max和Min是全局变量,是公用的,它的值可以供各个函数使用,如果在一个函数中,改变了它的值,在其它函数中可以使用这个已改变的值。使用全局变量可以减少函数参数的个数,从而减少内存空间以及传递数据是的时间消耗。,float average(float array ,int n)int i;float aver,sum=array0;Max=Min=array0;for(i=1;iMax)Max=arrayi;else if(arrayiMin)Min=arrayi;sum=sum+arrayi;aver=sum/n
20、;return(aver);,2、建议不在必要时不要使用全局变量。原因如下:、全局变量在程序的全部执行过程中都占用存储单元。、它使函数的通用性降低了。降低了程序的可靠性和通用性。、使用全局变量过多,会降低程序的清晰性。,#include int a=3,b=5; void main() int max(int a,int b);int a=8;printf(“%dn“,max(a,b); int max(int a,int b) int c;c=ab?a:b;return(c); 运行结果为:8,3、如果在同一个源文件中,外部变量与局部同名,则在局部变量的作用范围内,外部变量不起作用。,右边程
21、序例中,在第二行定义外部变量a和b,并初始化。在main中定义局部变量a,因此全局变量在main中,max(a, b)相当于max(8, 5)而不是相当于max(3, 5)。在max中,a和b是形参,是局部变量,它们的值是由实参传来的。,7.9 变量的存储方式和生存期,作用域是变量的一个重要属性。按作用域不同,变量可以分为全局变量和局部变量。,变量还有一个重要属性:变量的生存期,即变量值存在的时间。根据变量生存期的不同,可以将变量分为动态存储变量和静态存储变量。动态存储是指在程序运行期间根据需要动态分配存储空间的存储方式,即需要时就分配存储空间,不需要时就释放。如形式参数就属于动态存储变量。静
22、态存储是指在程序运行期间分配固定的存储空间的存储方式。如全局变量就属于静态存储变量。,内存中供用户使用的存储空间可以分为三个部分:1)、程序式区;2)、静态存储区;3)、动态存储区;,数据分别存放在动态存储区和静态存储区中。全局变量全部存放在静态存储区中,在程序开始执行时给全局变量分配存储区,程序执行完毕就释放。在程序执行过程中它们占据固定的存储单元,而不是动态地进行分配和释放。,在动态存储区中存放以下数据:、函数形式参数。在调用函数时给形参分配存储空间。、函数中定义的未加static声明的变量,即自动变量。、函数调用时的现场保护和返回地址。对以上这些数据,在函数调用时分配动态存储空间,函数结
23、束时释放这些空间。,7.9.1 局部变量的存储类别,1、自动变量(auto变量)函数中的局部变量,如果不专门声明为static(静态)存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。在调用该函数时,系统会给这些变量分配存储空间,在函数调用结束时就自动释放这些存储空间。因此这类局部变量称为自动变量。,Int f (int a,) /* 定义f 函数,a为 形参a */ auto int b,c=3; /* 定义b, c o为自动变量 */ ,实际上,关键字“auto”可以省略。Auto不写则隐含确定为“自动存储类别”。,2、建议不在必要时不要使用全局变量。原因如下:、全局变量在程序的全部执行过程中都占用存储单元。、它使函数的通用性降低了。降低了程序的可靠性和通用性。、使用全局变量过多,会降低程序的清晰性。,#include float Max=0,Min=0; void main() float average(float array ,int n);float ave,score10;int i;for(i=0;i10;i+)scanf(“%f“,