《(中职)计算机程序设计(C语言)第七章第4节教学课件工信版.ppt》由会员分享,可在线阅读,更多相关《(中职)计算机程序设计(C语言)第七章第4节教学课件工信版.ppt(34页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、YCF(中职)计算机程序设计(C语言)第七章第4节教学课件工信版第七章第七章 函数函数7.4函数的调用函数的调用7.4函数的调用函数的调用7.4.1函数调用的一般形式函数调用的一般形式7.4.2函数调用的方法函数调用的方法7.4.3被调函数的声明和函数原型被调函数的声明和函数原型7.4.4函数的嵌套调用函数的嵌套调用7.4.5函数的递归调用函数的递归调用7.4.6数组作为函数参数数组作为函数参数7.4.1函数调用的一般形式函数调用的一般形式前面已经说过,在程序中是通过对函数的调用来执行函数体的,其过程与其它语言的子程序调用相似。C语言中,函数调用的一般形式为:函数名(实际参数表)对无参函数调用
2、时则无实际参数表。实际参数表中的参数可以是常数,变量或其它构造类型数据及表达式。各实参之间用逗号分隔。7.4.2函数调用的方法函数调用的方法在C语言中,可以用以下几种方式调用函数:(1)函数表达式:函数作为表达式中的一项出现在表达式中,以函数返回值参与表达式的运算。这种方式要求函数是有返回值的。例如:z=max(x,y)是一个赋值表达式,把max的返回值赋予变量z。(2)函数语句:函数调用的一般形式加上分号即构成函数语句。例如:printf(%d,a);scanf(%d,&b);都是以函数语句的方式调用函数。(3)函数实参:函数作为另一个函数调用的实际参数出现。这种情况是把该函数的返回值作为实
3、参进行传送,因此要求该函数必须是有返回值的。例如 printf(%d,max(x,y);即是把max调用的返回值又作为printf函数的实参来使用的。在函数调用中还应该注意的一个问题是求值顺序的问题。所谓求值顺序是指对实参表中各量是自左至右使用呢,还是自右至左使用。对此,各系统的规定不一定相同。【例7.7】main()int i=8;printf(%dn%dn%dn%dn,+i,-i,i+,i-);结果:如按照从右至左的顺序求值。运行结果应为:8 7 7 8如对printf语句中的+i,-i,i+,i-从左至右求值,结果应为 9 8 8 9 应特别注意的是,无论是从左至右求值,应特别注意的是,
4、无论是从左至右求值,还是自右至左求值,其输还是自右至左求值,其输出顺序都是不变的,出顺序都是不变的,即输出顺序总是和实参表中实参的顺序相同。即输出顺序总是和实参表中实参的顺序相同。7.4.3被调函数的声明和函数原型被调函数的声明和函数原型 在主调函数中调用某函数之前应对该被调函数进行说明(声明),这与使用变量之前要先进行变量说明是一样的。在主调函数中对被调函数作说明的目的是使编译系统知道被调函数返回值的类型,以便在主调函数中按此种类型对返回值作相应的处理。其一般形式为:类型说明符 被调用函数名(类型 形参,类型 形参);或为:类型说明符 被调用函数名(类型 ,类型);括号内给出了形参的类型和形
5、参名,或只给出形参类型。这便于编译系统进行检错,以防止可能出现的错误。【例7.5】的main函数中对max函数的说明为:int max(int a,int b);或写为:int max(int,int);C语言中又规定在以下几种情况时可以省去主调函数中对被调函数的函数说明。(1)如果被调函数的返回值是整型或字符型时,可以不对被调函数作说明,而直接调用。这时系统将自动对被调函数返回值按整型处理。例7.6的主函数中未对函数s作说明而直接调用即属此种情形。(2)当被调函数的函数定义出现在主调函数之前时,在主调函数中也可以不对被调函数再作说明而直接调用。例如例7.5中,函数max的定义放在main 函
6、数之前,因此可在main函数中省去对max函数的函数说明int max(int a,int b);(3)对库函数的调用不需要再作说明,但必须把该函数的头文件用include命令包含在源文件前部。(4)如在所有函数定义之前,在函数外预先说明了各个函数的类型,则在以后的各主调函数中,可不再对被调函数作说明。例如:char str(int a);float f(float b);main()char str(int a)float f(float b)其中第一,二行对str函数和f函数预先作了说明。因此在以后各函数中无须对str和f函数再作说明就可直接调用。【例7.8】计算s=22!+32!解析:本
7、题可编写两个函数,一个是用来计算平方值的函数f1,另一个是用来计算阶乘值的函数f2。主函数先调f1计算出平方值,再在f1中以平方值为实参,调用 f2计算其阶乘值,然后返回f1,再返回主函数,在循环程序中计算累加和。long f1(int p)int k;long r;long f2(int);k=p*p;r=f2(k);return r;long f2(int q)long c=1;int i;for(i=1;i=q;i+)c=c*i;return c;main()int i;long s=0;for(i=2;i1)按公式可编程如下:main()int n;long y;printf(ninp
8、ut a inteager number:n);scanf(%d,&n);y=func(n);printf(%d!=%ld,n,y);long func(int n)long f;if(n0)printf(n0,input error);else if(n=0|n=1)f=1;else f=func(n-1)*n;return(f);/*或者可以写成:return f;*/解析:程序中给出的函数func是一个递归函数。主函数调用func 后即进入函数func执行,如果n0,n=0或n=1时都将结束函数的执行,否则就递归调用func函数自身。由于每次递归调用的实参为n-1,即把n-1的值赋予形参
9、n,最后当n-1的值为1时再作递归调用,形参n的值也为1,将使递归终止。然后可逐层退回。下面我们再举例说明该过程。设执行本程序时输入为5,即求5!。在主函数中的调用语句即为y=func(5),进入func函数后,由于n=5,不等于0或1,故应执行f=func(n-1)*n,即f=func(5-1)*5。该语句对func作递归调用即func(4)。进行四次递归调用后,func函数形参取得的值变为1,故不再继续递归调用而开始逐层返回主调函数。func(1)的函数返回值为1,func(2)的返回值为1*2=2,func(3)的返回值为2*3=6,func(4)的返回值为6*4=24,最后返回值fun
10、c(5)为24*5=120。【例7.9-2】利用递归算法求解斐波拉切数列的第20项,已知该数列的第1项是0,第2项是1,从第3项开始是前两项之和。即:f0=0,f1=1,fn=fn-1+fn-2。程序编写如下:#include int f(int n)if(n=1|n=2)return 1;else return(f(n-2)+f(n-1);int main()int i,num;printf(请输入num的值:);scanf(%d,&num);for(i=1;i=num;i+)printf(%-6d,f(i);if(i%5=0)printf(n);printf(n);return 0;本例采
11、用递归算法显得尤为简单,而且本例采用递归算法显得尤为简单,而且比较好理解。即已知第比较好理解。即已知第1项为项为0,第,第2项为项为1,从第,从第3项起是前两项之和,项起是前两项之和,f2=f0+f1=0+1=1,所以条件可以设置成当,所以条件可以设置成当n=1或者或者n=2时,返回时,返回1。其它项则依。其它项则依次递归调用即可。次递归调用即可。【例7.9-3】采用非递归方法求解斐波拉切数列#includevoid main()int i,num;int f=0,1;printf(请输入num的值:);scanf(%d,&num);for(i=2;i=num;i+)fi=fi-2+fi-1;
12、for(i=0;i0)printf(%d,v);else printf(%d,0);main()int a5,i;printf(input 5 numbersn);for(i=0;i5;i+)scanf(%d,&ai);nzp(ai);本程序中首先定义一个无返回本程序中首先定义一个无返回值函数值函数nzp,并说明其形参,并说明其形参v为整为整型变量。在函数体中根据型变量。在函数体中根据v值输出值输出相应的结果。在相应的结果。在main函数中用一函数中用一个个for语句输入数组各元素,每输语句输入数组各元素,每输入一个就以该元素作实参调用一入一个就以该元素作实参调用一次次nzp函数,即把函数,即
13、把ai的值传送给的值传送给形参形参v,供,供nzp函数使用。函数使用。解析:解析:(2)数组名作为函数参数用数组名作函数参数与用数组元素作实参有几点不同:1、用数组元素作实参时,只要数组类型和函数的形参变量的类型一致,那么作为下标变量的数组元素的类型也和函数形参变量的类型是一致的。因此,并不要求函数的形参也是下标变量。换句话说,对数组元素的处理是按普通变量对待的。用数组名作函数参数时,则要求形参和相对应的实参都必须是类型相同的数组,都必须有明确的数组说明。当形参和实参二者不一致时,即会发生错误。【例7.11】数组a中存放了一个学生5门课程的成绩,求平均成绩。float aver(float a
14、5)int i;float av,s=a0;for(i=1;i5;i+)s=s+ai;av=s/5;return av;void main()float sco5,av;int i;printf(ninput 5 scores:n);for(i=0;i5;i+)scanf(%f,&scoi);av=aver(sco);printf(average score is%5.2f,av);本程序首先定义了一个本程序首先定义了一个实型函数实型函数aver,有一个形,有一个形参为实型数组参为实型数组a,长度为,长度为5。在函数在函数aver中,把各元素中,把各元素值相加求出平均值,返回值相加求出平均值,
15、返回给主函数。主函数给主函数。主函数main 中首先完成数组中首先完成数组sco的输的输入,然后以入,然后以sco作为实参作为实参调用调用aver函数,函数返回函数,函数返回值送值送av,最后输出,最后输出av值。值。从运行情况可以看出,程从运行情况可以看出,程序实现了所要求的功能。序实现了所要求的功能。3、前面已经讨论过,在变量作函数参数时,所进行的值传送是单向的。即只能从实参传向形参,不能从形参传回实参。形参的初值和实参相同,而形参的值发生改变后,实参并不变化,两者的终值是不同的。而当用数组名作函数参数时,情况则不同。由于实际上形参和实参为同一数组,因此当形参数组发生变化时,实参数组也随之
16、变化。当然这种情况不能理解为发生了“双向”的值传递。但从实际情况来看,调用函数之后实参数组的值将由于形参数组值的变化而变化。2、在普通变量或下标变量作函数参数时,形参变量和实参变量是由编译系统分配的两个不同的内存单元。在函数调用时发生的值传送是把实参变量的值赋予形参变量。在用数组名作函数参数时,不是进行值的传送,即不是把实参数组的每一个元素的值都赋予形参数组的各个元素。因为实际上形参数组并不存在,编译系统不为形参数组分配内存。那么,数据的传送是如何实现的呢?在我们曾介绍过,数组名就是数组的首地址。因此在数组名作函数参数时所进行的传送只是地址的传送,也就是说把实参数组的首地址赋予形参数组名。形参
17、数组名取得该首地址之后,也就等于有了实在的数组。实际上是形参数组和实参数组为同一实际上是形参数组和实参数组为同一数组,共同拥有一段内存空间。数组,共同拥有一段内存空间。图中设图中设a为实参数组,类型为整型。为实参数组,类型为整型。a占有以占有以2000为首地址的一块内存区。为首地址的一块内存区。b为形参数组名。当发为形参数组名。当发生函数调用时,进行地址传送,把实参数组生函数调用时,进行地址传送,把实参数组a的的首地址传送给形参数组名首地址传送给形参数组名b,于是,于是b也取得该地也取得该地址址2000。于是。于是a,b两数组共同占有以两数组共同占有以2000为为首地址的一段连续内存单元。从图
18、中还可以看首地址的一段连续内存单元。从图中还可以看出出a和和b下标相同的元素实际上也占相同的两个下标相同的元素实际上也占相同的两个内存单元内存单元(整型数组每个元素占二字节整型数组每个元素占二字节)。例如。例如a0和和b0都占用都占用2000和和2001单元,当然单元,当然a0等于等于b0。类推则有。类推则有ai等于等于bi。【例7.12】题目同7.10例。改用数组名作函数参数。main()int b5,i;printf(ninput 5 numbers:n);for(i=0;i5;i+)scanf(%d,&bi);printf(initial values of array b are:n)
19、;for(i=0;i5;i+)printf(%d,bi);nzp(b);printf(nlast values of array b are:n);for(i=0;i5;i+)printf(%d,bi);void nzp(int a5)int i;printf(nvalues of array a are:n);for(i=0;i5;i+)if(ai=0)ai=0;printf(%d,ai);解析本程序中函数nzp的形参为整数组a,长度为5。主函数中实参数组b也为整型,长度也为5。在主函数中首先输入数组b的值,然后输出数组b的初始值。然后以数组名b为实参调用nzp函数。在nzp中,按要求把负值
20、单元清0,并输出形参数组a的值。返回主函数之后,再次输出数组b的值。从运行结果可以看出,数组b的初值和终值是不同的,数组b的终值和数组a是相同的。这说明实参形参为同一数组,它们的值同时得以改变。用数组名作为函数参数时还应注意以下几点:形参数组和实参数组的类型必须一致,否则将引起错误。形参数组和实参数组的长度可以不相同,因为在调用时,只传送首地址而不检查形参数组的长度。当形参数组的长度与实参数组不一致时,虽不至于出现语法错误(编译能通过),但程序执行结果将与实际不符,这是应予以注意的。【例7.13】如把【例7.12】修改如下:void nzp(int a8)int i;printf(nvalue
21、s of array aare:n);for(i=0;i8;i+)if(ai0)ai=0;printf(%d,ai);main()int b5,i;printf(ninput 5 numbers:n);for(i=0;i5;i+)scanf(%d,&bi);printf(initial values of array b are:n);for(i=0;i5;i+)printf(%d,bi);nzp(b);printf(nlast values of array b are:n);for(i=0;i5;i+)printf(%d,bi);本程序与例本程序与例7.12程序比,程序比,nzp函数的形参
22、数组长度函数的形参数组长度改为改为8,函数体中,函数体中,for语句的循环条件也改为语句的循环条件也改为i8。因此,形参数组。因此,形参数组a和实参数组和实参数组b的长度不的长度不一致。编译能够通过,但从结果看,数组一致。编译能够通过,但从结果看,数组a的的元素元素a5,a6,a7显然是无意义的。显然是无意义的。在函数形参表中,允许不给出形参数组的长度,或用一个变量来表示数组元素的个数。例如,可以写为:void nzp(int a)或写为void nzp(int a,int n)其中形参数组a没有给出长度,而由n值动态地表示数组的长度。n的值由主调函数的实参进行传送。由此,例由此,例7.13又
23、可改为例又可改为例7.14的形式。的形式。由此,例由此,例7.13又可改为例又可改为例7.14的形式。的形式。void nzp(int a,int n)int i;printf(nvalues of array a are:n);for(i=0;in;i+)if(ai0)ai=0;printf(%d,ai);main()int b5,i;printf(ninput 5 numbers:n);for(i=0;i5;i+)scanf(%d,&bi);printf(initial values of array b are:n);for(i=0;i5;i+)printf(%d,bi);nzp(b,5);printf(nlast values of array b are:n);for(i=0;i5;i+)printf(%d,bi);解析本程序nzp函数形参数组a没有给出长度,由n 动态确定该长度。在main函数中,函数调用语句为nzp(b,5),其中实参5将赋予形参n作为形参数组的长度。多维数组也可以作为函数的参数。在函数定义时对形参数组可以指定每一维的长度,也可省去第一维的长度。因此,以下写法都是合法的。int MA(int a310)或int MA(int a 10)。
限制150内