教学大纲第六章工程中的函数与变量[目的要求]1.掌握函数的定义、说明和调用方法2.理解函数调用过程中的参数传递3.了解函数的递归调用4.了解函数指针变量与指针型函数5.理解main()函数参数的作用6.理解变量的存储类型、作用域、生存周期7.理解内部函数与外部函数8.了解预处理过程9.掌握文件包含、宏定义,理解条件编译10.了解多文件程序的设计要点[基本内容]1.函数的定义与说明2.函数的调用3.函数的递归调用(选学)4.指针与函数(选学)问题引出读多少行的程序能让你不头疼?main()当中能放多少行程序?假如printf()函数由10行代码替换,那么你见过的程序会成什么样子?如果所有代码都在main()当中,怎么团队合作?如果代码都在一个文件中,怎么团队合作?函数(function)和模块(module)函数是C语言中模块化编程的最小单位–可以把每个函数看作一个模块若干相关的函数可以合并作一个“模块”main()printf()scanf()power()putchar()getchar()main()stdio:printf()scanf()putchar()getchar()mymdl:power()6.1函数定义与说明c语言程序是由函数组成的,c语言不仅提供了极为丰富的库函数,还允许用户建立自己定义的函数。从函数定义的角度看,函数可分为库函数和用户自定义函数两种。1、库函数由系统提供的函数,用户无须定义,也不必在程序中进行类型说明,只需在程序头部将包含该函数原型的头文件进行预定义说明。如printf()函数、getchar()函数、putchar函数和scanf()函数。2、用户自定义函数用户自定义函数是用户按需要自己写的函数,如:longpower(intx,intn){inti;longp;for(i=1,p=1;i=n;i++)p*=x;returnp;}#include“stdio.h”main(){intw=2;longr;r=power(w,2)+power(w,3);printf(“result=%ld\n”,r);}功能是求xn6.1.1函数定义1、函数定义形式函数定义不允许嵌套。在c语言中,所有函数(包括主函数main())都是平行的。函数可在程序中任意位置定义,但不能定义在另一个函数的内部。任何函数(包括主函数main())都是由函数说明和函数体两部分组成,可分为无参函数和有参函数两种。[函数类型]函数名([void]){说明语句部分;执行语句部分;}无参数传递(1)无参函数的一般形式例如:voidprintstar(){printf(“***”);}[函数类型]函数名(类型参数1[,类型参数2,….]){函数体;[return表达式;]}(2)有参函数的一般形式intmax(intx,inty){intz;z=xy?x:y;return(z);}2、函数的返回值与函数类型(1)函数返回值与return语句函数可分为有返回值和无返回值函数两种:有返回值在c语言中,用return语句实现函数值的返回,格式:return(返回值表达式);功能:返回到调用函数,并将“返回值表达式”的值赋给调用函数。无返回值用void将函数定义为“无类型”或“空类型”。如:例:voidprintstar(){printf(“***”);}(2)函数类型函数类型是函数返回值的类型。通常,return语句中“返回值表达式”的类型应与所定义的函数类型一致,如果两者类型不同,则将“返回值表达式”的数值类型转换为函数类型。在定义函数时缺省函数类型,则系统一律按整型处理。main(){floata,b;intc;scanf(%f,%f,",&a,&b);c=max(a,b);printf("Maxis%d\n",c);}intmax(floatx,floaty){floatz;/*z为实型变量*/z=x>y?x∶y;return(z);}运行情况如下:1.5,2.5Maxis26.1.2函数说明调用自定义函数之前,应该在主调函数中说明被调函数的类型。•在主函数中对被调函数作类型说明,意在告诉编译系统,本函数中将要用到的某函数是什么类型,以便让编译系统作出相应的处理。•函数说明的一般形式为:类型函数名(类型形参,类型形参……);或类型函数名(类型,类型……);•注意:函数的类型说明是函数调用中一个非常重要的环节,忽略它将导致程序编译时出错。例:调用函数求n!。main(){intnum;longt;longf(intn);/*函数类型说明*/scanf(%d,&num);t=f(num);/*函数调用*/printf(%d!=%1d,num,t);}longf(intn)/*定义f函数,其功能是求n!*/{inti;longa=1;/*变量a存放阶乘*/for(i=1;i=n;i++)/*求阶乘*/a*=i;return(a);/*返回a的值*/}函数定义与函数说明的区别函数定义是指函数功能的确立,包括指定函数名、函数类型、形参及其类型、函数体等。它是一个完整的、独立的单位函数说明是对函数名、函数返回值类型、形参类型的说明,不包括形参和函数体。函数说明只起到一个声明作用。下面几种情况除外,可以不作函数说明:(1)被调函数的返回值是int时(2)被调函数的定义出现在主调函数之前时如:①被调函数在主调函数之后,需说明main(){longf();……t=f();…….}longf(intx,inty){…….}②被调函数在主调函数之前已定义,不需说明longf(intx,inty){}main(){t=f(a,b);}(3)在源程序的开头,在所有函数定义之前已说明了函数的类型floata();main(){………a(c,d);……..}floata(intx,inty){……..}如:(4)对库函数的调用不需要再进行说明,但必须把定义该函数的头文件用#include命令包含进来。形式参数和实际参数形式参数:在定义函数时函数名后面括弧中的变量名,简称形参。实际参数:在调用函数时函数名后面括弧中的表达式,简称实参。6.2函数调用例如6.2.1函数调用的一般形式1.函数调用的一般形式为:有参函数:函数名(实参表列);无参函数:函数名();2.有关规定:多个实参间用逗号隔开实参与形参间个数相等,类型应一致实参与形参按顺序对应,一一传递数据1.函数语句:把函数调用作为一个语句。此时不要求函数带回值,只要求函数完成一定的操作。例如:printstar();max(a,b);2.函数表达式:函数出现在一个表达式中,这种表达式称为函数表达式。这时要求函数带回一个确定的值以参加表达式的运算。例如:c=3+max(a,b);3.函数参数:(函数的嵌套调用)函数调用作为一个函数的实参。例如:m=max(a,max(b,c));函数调用的方式例如:定义一个函数,求一个数的n次方(n是大于0的整数)。程序代码:#includestdio.hlongpower(intx,intn){inti;longp;for(i=1,p=1;i=n;i++)p*=x;returnp;}main(){intw=2;longr;r=power(w,3);printf(Theresultis%ld.\n,r);}Theresultis8.6.2.2函数的嵌套调用与递归调用嵌套调用:在调用一个函数的过程中,又调用另一个函数。函数是相互平行的,C语言规定函数不能嵌套定义,但可以嵌套调用。1、函数的嵌套调用main函数调用A函数结束A函数调用B函数B函数192873456两层嵌套示意图计算s=(1*1)!+(2*2)!+(3*3)!+...+(6*6)!算法分析:n!可定义为一个函数fact(intn)(k*k)的运算也可以定义为一个函数mul(intk)计算s本身是一个循环程序longfact(intn){intj;longz=1;for(j=1;j=n;j++)z*=j;returnz;}intmul(intk){inta;longc;a=k*k;c=fact(a);returnc;}main(){inti;longs=0;for(i=1;i=6;i++)s=s+mul(i);printf(“%d\n”,s);}2、函数的递归调用函数的递归在调用函数的过程中,该函数又去调用函数自身main()sub()在被调用的函数中必然存在一个终止条件,否则会陷入死循环intf(intx){intz;if(x=10)returnx;z=3+f(2+x);return(z);}z=3+f(4)3+f(6)3+f(8)3+f(10)1013161922f(2)函数的调用和执行过程用递归法计算n!n!=1n=0n*(n-1)!n≠0longfact(intn){longz;if(n==0)z=1;elsez=n*fact(n-1);returnz;}main(){intk;longf;scanf(“%d”,&k);f=fact(k);printf(“%ld\n”,f);}fact(n)=n*fact(n-1)fact(4)4*fact(3)3*fact(2)2*fact(1)2*13*2*14*3*2*124fact(4)的执行过程6.2.3函数调用中的参数传递参数传递分为:1.值传递——函数参数为基本数据类型时2.地址传递——函数参数为数组名、指针类型时1、按值传递在函数的调用过程中有两种数据传递:(1)参数传递在调用时,将实参传递给形参;对于参数的传递,应该注意以下几点:实参的个数与类型必须与定义时保持一致,否则会出错,实参必须有确定的值,可以是常量,变量或表达式。在调用时将实参的值赋给形参变量。在函数调用前,形参不占内存单元,调用时占用,调用后释放。形参变量和实参变量占用不同的内存单元(传值)定义函数时,必须指定形参类型。(2)返回值的传递在调用结束时,有返回值的函数把函数的值返回到主调函数。例1熟悉函数调用时实参对形参的数值传递#includestdio.hvoids(intn){inti;printf(n_x=%d,,n);for(i=n;i=1;i-=50)n=n+i;printf(n_x=%d\n,n);}main(){intn=100;s(n);printf(n_s=%d\n,n);}运行结果:n_x=100,n_x=250n_s=100例2编写函数swap(x,y),实现交换x、y的值#includestdio.hvoidswap(int,int);main(){inta=10,b=20;printf(a1=%d,b1=%d\n,a,b);swap(a,b);printf(a2=%d,b2=%d\n,a,b);}voidswap(inta,intb){inttemp;temp=a;a=b;b=temp;}运行结果:a1=10,b1=20a2=10,b2=20那是为什么呢?2、地址传递如果用地址(数组名或指针变量)作为函数的参数,即把地址传递给函数,从而实现地址传递。运行结果:a1=10,b1=20a2=20,b2=10例:两个数的交换#includestdio.hvoidswap(int*x,int*y);main(){inta=10,b=20;printf(a1=%d,b1=%d\n,a,b);swap(&a,&b);printf(a2=%d,b2=%d\n,a,b);}voidswap(int*x,int*y){inttemp;temp=*x;*x=*y;*y=temp;}1020200A200C200Babxy运行结果:a1=10,b1=20a2=20,b2=10例有一个一维数组score,内放10个学生成绩,求平均成绩。程序如下:floataverage(floatarray[10]){inti;floataver,sum=array[0];for(i=1;i<10;i++)sum=sum+ar