第六章函数从用户角度看,C语句中的函数可以分为两种:(1)标准函数,即库函数。(2)用户自定义函数。6.1函数的定义6.1.1函数的定义•函数定义的形式为:类型说明符函数名(形式参数表列){声明部分;语句部分;}例如:intsum(inta,intb){intc;/*声明部分*/c=a+b;/*语句部分*/returnc;}这是一个求两个整数和的函数。6.1.2函数的参数•如果函数调用时需要传递数据,则在定义函数时在函数名后的括号内指明所需要的参数,包括参数类型和参数名。•在函数定义时指定的参数称为形式参数(简称“形参”),在函数调用中传递的数据称为实际参数(简称“实参”)。例6-1输出两个整数的最大值。intmax(intx,inty){intz;z=xy?x:y;returnz;}main(){inta,b,c;scanf(“%a%b”,&a,&b);c=max(a,b);printf(“maxis:%d”,c);}运行结果为:56↙maxis:6说明:1.函数的形参可以有多个,可以是相同类型也可以不同类型。intmax(intx,inty)2.形参必须是变量,但实参可以是常量、变量和表达式。3.实参类型必须与形参类型兼容。4.在C语言中,函数进行的是“值传递”,将实参的值传递给形参。这种传递是单向的.6.1.3函数的返回值•使用return语句将结果返回给调用程序。格式为:return(表达式);或者return表达式;•一个函数可以有多个return语句.•当遇到第一个return语句时,C立即终止函数的执行.•如果函数不需要返回计算结果,即不需要返回值时,最好定义为void类型.6.2函数的调用6.2.1函数的调用•函数调用的一般形式为:函数名(实参表列);•如果函数没有参数,括号内为空,但不能省略括弧。•如果有多个参数,参数之间使用逗号分隔。调用时,应保证实参的个数与形参相同,类型一致。例6-2计算1到n的整数和。intcalcu(intx){inti,total=0;for(i=1;i=x;i++)total=total+i;returntotal;}main(){intn;scanf(“%d”,&n);printf(“sum=%d”,calcu(n));}按照函数调用时出现的位置,可以分为以下三种调用方式:1.函数语句2.函数表达式3.作为函数参数6.2.2函数原型•函数声明的格式和函数定义的首部相同,并在其后添加分号表示语句结束。例如:intmax(intx,inty);intcalcu(intx);这种函数声明称为“函数原型”。•一般地,为了提供程序的可读性,把函数原型写在main函数之前,函数的定义写在main函数之后。但如果函数的定义出现在main函数之前,则可省略相应的函数原型。6.2.3函数的嵌套调用和递归•函数定义内部不能定义其他函数,即不能出现函数定义的嵌套。•但函数的调用是可以嵌套的。•函数可以嵌套调用。若函数调用自身,称为函数的递归调用。•递归必须有条件地进行。例6-3求n!分析:根据阶乘的性质可以有如下递归公式:例如5!,等于5*4!,而4!=4*3!,……,1!=1。floatfac(intn)/*求n的阶乘函数*/{floatf;if(n=1)f=1;/*条件满足,终止递归*/elsef=n*fac(n–1);/*否则继续递归*/returnf;/*返回阶乘值*/}例6-4求Fibonacci序列的第n项的值分析:Fibonacci序列的规律是:每个数等于前面两个数之和。由此可得如下公式:intfib(intn){if(n=2)return1;/*递归终止*/elsereturnfib(n–1)+fib(n–2);}6.3数组作为函数参数6.3.1数组元素作为函数参数例6-5找出数组的最大值。6.3.2数组名作为函数参数例6-6编写一个函数显示数组的值。6.3.3多维数组作为函数参数例6-7将一个4*4的矩阵转置。6.4变量的作用域•变量的作用域又称作用范围,指的是一个变量在何处可以使用。•根据变量的作用域可将变量分为局部变量和全局变量。6.4.1局部变量•局部变量是在函数内部声明的变量,这包括函数的形参。它们仅在包含该变量声明的函数中才起作用,在该函数外不能使用这些变量。例6-8局部变量示例voidlocal_value(){inta=1,b=2;printf(“%d%d”,a,b);}main(){inta=5;floatb=2.5;printf(“%d%f”,a,b);}由于局部变量只在本函数内有效,因此可以在程序的各个部分使用同名变量,同样,在参数传递时,实参也允许和形参同名。使用局部变量时要注意:(1)main函数中的变量也是局部变量。(2)可以在复合语句中定义变量,这样的变量只在复合语句内有效,这种复合语句也称为“分程序”或“程序块”。6.4.2全局变量•在函数外定义的变量称为全局变量,又称外部变量或全程变量。全局变量的有效范围从定义位置到文件结束,在这个范围内的任何地方都可使用(包括函数)。例6-9全局变量示例inta=5,b=10;/*全局变量*/voidmodify(){a++;b--;printf(“%d,%d\n”,a,b);}main(){modify();printf(“%d,%d\n”,a,b);}运行结果为:6,96,9例6-10全局变量与局部变量同名示例。inta=5,b=10;voidmodify(){inta=5,b=10;a++;/*修改的是局部变量a和b*/b--;printf(“%d,%d\n”,a,b);/*输出局部变量a和b*/}main(){modify();printf(“%d,%d\n”,a,b);/*输出全局变量a和b*/}运行结果为:6,95,106.5变量的存储类别6.5.1存储方式•静态存储方式•动态存储方式。6.5.2自动变量函数中的局部变量如果未加static声明就属于自动变量。这包括函数内的局部变量、函数形参和复合语句中定义的变量。自动变量是动态分配内存的,其值存储在动态存储区中。调用函数时为自动变量分配内存,调用结束释放内存。也可为自动变量添加auto关键字声明。6.5.3静态局部变量•如果局部变量使用static关键字声明,就属于“静态局部变量”。这种变量属于静态存储类型,在静态存储区分配内存。•程序运行开始(而不是函数被调用时)就给静态局部变量分配内存,运行结束释放。而自动变量是调用函数时才分配内存,调用结束释放内存。这是静态局部变量和自动变量的一个本质区别。例6-11静态局部变量示例voidsub(){inta=0;staticintb=0;a++;b++;printf(“%d,%d\n”,a,b);}main(){inti;for(i=1;i=3;i++)sub();}运行结果为:0,10,20,3使用静态局部变量时须注意以下几点:1.静态局部变量如果没有显式初始化,则系统自动为其初始化为零值。2.尽管静态局部变量在程序运行期间都存在,但它的作用域是函数内部。6.5.4寄存器变量•当需要频繁使用某一个变量时,从内存中读写该变量都需要耗费一定的时间。为了提高程序的性能,C语言允许将变量存储在CPU的寄存器中。•这种存储类别的变量称为“寄存器变量”,可以使用register关键字声明。•只有局部变量和形式参数才允许声明为寄存器变量。•register变量的使用与否意义不大。6.5.5外部变量•外部变量(全局变量)是在函数外部定义的,其作用域是从定义处开始到文件结束。系统对外部变量采用静态存储方式,即编译时分配内存,程序运行结束释放。•同静态变量类似,未显式初始化的外部变量初值是0。•如果在外部变量定义处之前需要使用该变量,则需要在使用之前使用extern关键字声明该变量,以表示该变量是一个已定义过的外部变量。例6-13extern关键字示例main(){externinta,b;/*外部变量声明*/++a;b+=a;printf(“%d,%d”,a,b);}inta=5,b=10;/*定义外部变量*/运行结果为:6,166.6函数应用实例例6-14将序列的最大值和第一个数交换,最小值和最后一个数交换,并输出新序列。例6-15编写一个函数,在一个整数序列中查找某个整数,若存在,返回该整数序列中的位置;否则返回-1。要求在主函数中调用,输出结果。本章小结•所有C函数的定义都是并列的,不能在一个函数内再说明或定义另一个函数。函数不能嵌套定义,但可嵌套调用,也可以递归调用。•函数通过调用被执行,调用时给出函数名和实参。实参的类型、个数、顺序要与形参一致。调用时,实参的值传递给实参。•如果数组名作为参数传递,那么实际传递的是数组的首地址。•如果函数定义在后、调用在前,则必须在调用语句前声明。声明时,最好使用函数原形进行声明,以确定被函数返值的类型和形式参数。•有了函数,可以实现模块化程序设计。定义一个函数后,可以进行多次函数调用。通过函数调用,将相互独立的函数间建立某种关联。