第7章函数本章要点熟练掌握函数的定义和调用方式理解和掌握函数参数的传递方式掌握变量的作用域和存储类型本章难点函数的参数调用对变量作用域和生存期的理解7.1函数的概念一、C函数的概念将一个C程序分为若干模块,每个模块实现一个特定的功能,在C语言中用函数来实现模块的功能。函数:具有某种功能的独立程序段。⑴从程序设计方法看C函数:它是实现模块化程序设计的语法元素。⑵从C语言中程序的组成方式看C函数:它是程序的基本组成单位。二、C函数与C程序结构⑴C程序由主函数(main函数)和若干个子函数构成;⑵主函数调用子函数;⑶子函数在定义时是并列的;⑷子函数可相互调用,也可被多次调用。mainabcdefghhiegmain()/*主函数*/{print_star();/*调用print_star函数画****/print_message();/*调用print_message函数写字*/print_star();/*调用print_star函数画****/}print_star()/*定义print_star函数*/{prinf(“\n**********”);}print_message()/*定义print_message函数*/{prinf(“\nHello!”);}函数调用例:运行结果:**********Hello!**********三、C函数的特点⑴一个源文件由一个或多个函数组成,它是一个独立编译单元。⑵一个C程序由一个或多个源文件组成;⑶C程序执行总是从main函数开始,调用其他函数后流程回到main函数,在main函数中结束整个程序的运行。⑷函数不能嵌套定义,但可以互相调用。注意不能调用main函数。四、函数的分类1.从函数的参数形式看,函数可分为两类:①无参函数;②有参函数有参函数例:(输出两数中大者)#includestdio.hintmax(intx,inty);main(){intnum1,num2,a;scnaf(%d,%d,&num1,&num2);a=max(num1,num2);printf(max=%d,a);}intmax(intx,inty){intz;if(xy)z=x;elsez=y;}2.从用户使用的角度看,函数有两种:①标准函数(库函数);②用户自己定义的函数库函数:是由编译系统提供的已设计好的函数,用户只需调用而无需要去实现它。前几章用过的scanf,printf,getchar,putchar等都是库函数。用户自定义函数:由程序员自己定义和设计的函数。需要程序员自己来编写函数功能实现代码。7.2函数定义的一般形式一、无参函数的定义形式类型标识符函数名(){说明部分语句}例如:print_message(){printf(“\nHello!”);}无参数传递二、有参函数的定义形式类型标识符函数名(形式参数表列){说明部分语句}有参数传递例如:intmax(intx,inty)/*求x和y二者中大者,x,y为形参*/{intz;/*函数体中变量的说明*/z=xy?x:y;return(z);/*将z的值作为函数返回值*/}三、空函数的定义形式类型标识符函数名(){}例如:dummy(){}功能:调用后什么也不做。用处:建立程序结构,在需要时补充功能。四、对形参说明的传统方式上面讲的形式参数表的说明形式是新版C语言表示形式(现代方式),即形式参数说明是类型和参数在一起说明。传统的形式参数说明是类型和参数分别说明。如:按传统方式说明形参intmax(x,y)intx,y;{……}按现代方式说明形参intmax(intx,inty){……}这两种方式都可以使用,但推荐使用现代方式。注意这里是逗号7.3函数的参数和返回值一、形式参数和实际参数形式参数:在定义函数时函数名后面括弧中的变量名,简称形参。实际参数:在调用函数时函数名后面括弧中的表达式,简称实参。main(){inta,b,c,m;scanf(“%d,%d,&a,&b);m=max(a,b,c);printf(“Maxis%d”,m);}intmax(intx,inty,intz){intm;m=xy?x:y;m=mz?m:z;return(m);}形参表实参表7.2.1函数的参数上例中形参与实参、函数名与返回值之间的关系:c=max(a,b);-----------------------实参:在运行时把函数的max(intx,inty)把值传给函数。结果赋给{………函数名returu(z);形参:通知系统}要预留内存位置。⑷实参可以是常量、变量或表达式,并且必须有确定的值;⑶对每个形参必须指明其名字和数据类型;⑸实参个数、类型必须与对应的形参一致;⑵形参是函数的内部变量,只在函数内部才有意义;⑹实参对形参的数据传递是值传递,即单向传递,只由实参传递给形参,反之不可。调用结束后,只有形参单元被释放,实参单元中的值不变。⑴形参调用前不占内存单元,调用时占用,调用后释放;关于参数的几点说明:二、函数的返回值1.定义:通过函数调用使主调函数得到一个确定的值,称为函数的返回值。例如:c=max(3,5);此时函数的返回值是5,因此c=52.函数的返回值语句return函数的返回值是通过return语句获得的。return语句将被调函数的一个确定的值带回主调函数中去。return语句的一般形式:return(函数返回值);return函数返回值;“函数返回值”是有确定值的常量、变量或表达式。说明:⑵一个函数中可以有多个return语句,但是一次函数执行只能执行其中的一个。当执行到某个return语句时,则终止函数执行,并带回函数值。⑶若函数体内没有return语句,就一直将函数执行完,再返回调用函数,有一个不确定的值带回。⑴return后面的值可以是一个表达式,例如:z=xy?x:y;return(z);return(xy?x:y);⑷return后面可以无“返回值”(即return;),则该return语句只起到终止函数执行,返回主调函数的作用。三、函数值的类型函数定义时应该指定函数的类型(即函数值的类型),应该与return语句的类型一致。说明:⑴凡不加类型说明的函数,一律自动按整型处理。⑵如果函数类型和return语句的类型不一致,以函数类型为准。对数值型数据,可以自动进行类型转换。既函数类型决定返回值的类型。⑶如果函数不返回值,可以将函数定义为“无类型”void(或称“空类型”)。例如:voidprint_star(){……}7.4函数的调用一、函数调用的一般形式函数名(实际参数表)有参数函数:无参数函数:函数名()说明:⑵实际参数表中的参数可以是常量、变量或表达式;⑶实参与形参个数相等,类型应一致;⑴多个实参间用逗号隔开;⑷实参与形参按顺序对应,一一传递数据;⑸实参表求值的顺序与系统有关。二、函数调用的具体方式1.把函数调用作为一个语句一般形式:函数名(实际参数表);这种方式常用于调用一个可以忽略返回值或没有返回值的函数。使用情况:如:我们程序中对scanf函数和printf函数的调用。2.函数调用出现在表达式(函数表达式)中。一般形式:变量名=函数表达式使用情况:这种方式用于调用带返回值的函数,函数的返回值将参加表达式的运算。如:a=3+max(num1,num2);注意:无返回值函数的调用,不能出现在表达式中。三、对被调用函数的说明(声明)⒈对被调用函数说明的前提条件被调用函数必须是已存在的函数,如用户自定义函数或库函数。2.被调用函数是用户自定义函数的函数说明同变量一样,函数的调用也应该遵循“先说明,后使用”的原则。如果使用用户自定义函数,而且主调函数和被调用函数在同一个文件中,应该在主调函数中说明被调函数的类型。其说明格式的一般形式如下:一般形式:类型标识符函数名(类型1形参1,类型2形参2,……);类型标识符函数名(类型1,类型2,……);或在C语言中,以上的函数说明称为函数原型。main(){floatadd(floatx,floaty);floata,b,c;scanf(“%f,%f”,&a,&b);c=add(a,b);printf(“sumis%f”,c);}/*定义add函数*/floatadd(floatx,floaty){floatz;z=x+y;return(z);}对被调用函数的说明作为表达式被调用3.被调用函数是库函数的函数说明如果使用库函数,需要在文件的开头用#include命令将需要的库函数包含到文件中。现在我们清楚了,为什么在使用库函数之前必须包含相应的头文件?那是因为对这些库函数的原型说明全部都写在对应的头文件里了。我们现在用到的头文件有:#include“stdio.h”/*调用输入输出函数*/#include“math.h”/*调用数学函数*/#include“string.h”/*调用字符,字符串函数*/在本书的附录C中列出了C语言可以使用的标准库函数,大约有75个。请在阅读时注意形参的要求、功能及返回值。4.函数说明和函数定义的区别函数说明的作用是把函数的名字、函数类型以及形参的类型、个数和顺序通知编译系统,以便在调用该函数时系统按此进行对照检查。旧版C语言函数说明只是对函数名极其返回类型的说明。如:floatmax();未进行全面检查,新版C语言兼容这种用法,但不提倡使用。因此,下列函数说明都是合法的:floatmax(intx,inty);floatmax(int,int);floatmax();函数定义是指对函数功能的确立,包括指定函数名、函数值类型、形参及其类型、函数体等,它是一个完整的、独立的函数单位。5.可省略被调用函数说明的三种情况⑴函数的返回值为整型或字符型时,可以不进行类型说明,系统按整型处理。main(){inta,b,c;scanf(“%d,%d”,&a,&b);c=max(a,b);printf(“Maxis%d”,c);}max(intx,inty){intz;z=xy?x:y;return(z);}无函数说明整型函数⑵被调用函数定义在主调函数之前,可以不进行类型说明。floatadd(floatx,floaty){floatz;z=x+y;return(z);}main(){floata,b;scanf(“%f,%f,“&a,&b);printf(“sumis%f”,add(a,b));}被调函数在主调函数之前主调函数在被调函数之后⑶如果已在所有函数定义之前,在文件的开头,在函数的外部已说明了函数类型,则在各个主调函数中不必对所调用的函数再做说明。charletter(char,char);floatf(float,float);inti(float,float);main(){…….}charletter(charc1,charc2){……}floatf(floatx,floaty){……}inti(float,float){……}在所有函数之前说明函数类型此处不必说明定义函数letter、f和i7.5数组作为函数的参数数组元素可以做函数的实参数组名可以做函数的参数多维数组可以做函数参数一、数组元素可以做函数的实参由于表达式可以做实参,数组元素可以作为表达式的组成部分,因此,数组元素可以做函数的实参,并且可以单向传递给形参。例:有两个数组A,B。各有10个元素,将它们逐个对应相比,如果A数组中的元素大于B数组中相应的元素数目多于B数组中的元素大于A数组中相应的元素数目,则认为A数组大于B数组,并分别统计出两个数组相应元素大于,小于和等于的个数。程序设计:函数large(x,y):比较两个数组元素的大小。返回值flag=1当xy0当x=y-1当xy主函数:输入两个数组,调用large函数比较,计数,输出统计结果。程序如下:main(){inta[10],b[10],i,n=0,m=0,k=0;printf(“enterarraya:\n”);for(i=0;i10;i++)scanf(“%d”,&a[i]);p