第五章函数本章目标1.进一步理解多个函数构成一个C程序2.进一步了解和熟悉库函数3.学会编写自已的函数4.理解函数的调用关系5.理解函数中参数的传递机制一、C程序模块一个C程序可由一个main()函数和多个其它函数构成。§5.1C程序模块其它函数指的是:系统函数由系统提供,放在头文件中自编函数由程序设计人员编写二、函数的调用如:mainfunafunbfuncfundfunxfuny所有的函数都具有两种特征:返回值参数(返回值和参数都是可缺省的)§5.2库函数一、库函数简介系统自带的标标准库函数根据不同的功能作用放在不同的头文件中。如:stdio.h用于标准输入/输出math.h用于数学计算ctype.h用于字符处理string.h用于字串处理time.h用于时间/日期的处理dir.h用于控制目录和路径graphics.h用于图形操作dos.hbios.h用于接口处理二、库函数的作用使程序更具有“独立性”和“可移植”性。使用时只需在程序的开头加上一条语句:#include.h“”根据使用的函数来确定使程序具有“模块化”。例:求两个浮点型数值x/y的余数。(注意:不能写成3.6%1.7=2.0)#includestdio.h#includemath.hmain(){floatx,y,z;scanf(%f%f,&x,&y);z=fmod(x,y);printf(z=%f,z);}例:函数的结构main(){printstar();print_message();printstar();}printstar(){printf(“********************************”);}print_message(){printf(“howdoyoudo!\n”);}对函数的理解:•一个源程序由一个或多个函数组成。•一个c程序由一个或多个源程序组成。•c程序的执行顺序从main()开始,调用其他函数后,流程回到main()函数。•c程序的所有的函数都是平行的,不存在函数的嵌套定义。•从用户的角度对函数的分类:(1)标准库函数:由系统提供。(2)用户自定义函数:解决用户的专门需要。•从函数的形参对函数的分类:(1)有参数函数:用于执行指定的一组操作,可以带回或不带回函数值。(2)无参数函数:主调函数可以将数据传给被调函数使用,被调函数中的数据也可以带回给主调函数使用。§5.3自定义函数一、自定义函数的作用使程序具有“积木”功能。(模块化)使程序具有“重构”功能。deamfghbgicmain1、含义:函数是完成一个独立功能的程序代码。2、C程序的基本结构(如图):1、函数的返回值是通过函数中的return语句来完成的,return语句一次只能返回一个值;2、由return语句返回的数据类型应与函数定义时的类型一致。例:3、当在函数的定义时,未指出函数的类型,c语言默认的类型为整型.4、函数没有return语句并不表明函数没有返回值,只是这个返回值对于调用者来说可能是没有用的,这个返回值随函数的不同它的值也不尽相同.5、如果我们明确表示函数不能返回值,在函数的定义时用“void”加以说明,这样可以确保函数不带回返回值.returnz等价return(z)return(xy?x:y);6、例:函数的返回值例:对函数返回值的理解:main(){inta,b,c;a=printstar();b=print_message();c=printstar();printf(“%d,%d,%d\n”,a,b,c);}若不需要函数带回任何值,可使用:voidprintstar(){…}voidprint_message(){…}例:函数返回值的类型与其定义的类型不符时:main(){floata,b;intc;scanf(“%f,%f”,&a,&b);c=max(a,b);printf(“maxis%d\n”,c);}max(floatx,floaty)输入:1.5,2.5{floatz;输出:maxis2z=xy?x:y;return(z);}/*return(xy?x:y)等价*/函数的一般调用1、函数调用的一般形式:函数名(实参表列);如:add(intx,inty)的调用为add(2,3)2、函数调用的规则:实参与形参在个数与类型上必须保持一致。–当函数无参数时,实参表列就为空,但括号不能少。–实参表列中的各个参数之间用逗号隔开。–实参与形参是一一对应的关系(函数调用的过程)。–对于实参表的求值顺序各个系统是不一致的。1.函数语句:功能:将函数调用作为一个语句。如:printf(“book”);要点:不要求函数带回值,只要求完成一定的操作。2.函数表达式:功能:将函数作为表达式的一部分。如:c=2*max(a,b);要点:要求函数带回一个确定的值,以参加表达式的运算。3.一个函数作为另一个函数的参数:功能:将函数的调用作为一个函数的实参。如:m=max(a,max(b,c));要点:其实质也是函数表达式的调用。函数调用的方式1.被调用函数必须是一个已经存在的函数.2.如果使用库函数应在文件头加上#include命令,以便将有关的库函数包含到本源文件中来.math.h3.对于用户自定义函数,函数的定义部分应出现在该函数被调用之前.不然的话,在调用函数之前应作提前说明.4.引用性说明的方法:一般形式:函数类型函数名(形参类型[形参名],…..);或函数类型函数名();作用:告诉编译系统函数类型、参数个数及类型,以便检验返回值类型函数名()如:intadd();函数的引用性说明例1:求二个数中的最大值intmax(x,y)intx,y;{intz;z=xy?x:y;return(z);}用return语句,返回函数的值。或:intmax(intx,inty){intz;z=xy?x:y;return(z);}四、举例通过函数调用求二个数中的最大值:#includestdio.hintmax(int,int);/*函数原型*/main(){inta,b,t;scanf(%d%d,&a,&b);t=max(a,b);/*函数调用*/printf(valueofmaximizeis:t=%d,t);}intmax(intx,inty)/*函数定义*/{intz;z=xy?x:y;return(z);}#includestdio.hfloatadd();/*函数说明*/main(){floata,b,c;scanf(%f,%f,&a,&b);c=add(a,b);/*函数调用*/printf(sum=%f;c);}floatadd(floatx,floaty);/*函数定义*/{floatz;z=x+y;returnz;}求二实数之和规则:若函数的定义放在main()函数之前可省略函数说明。第五章函数#includestdio.hfloatadd(floatx,floaty);/*函数定义*/{floatz;z=x+y;returnz;}main(){floata,b,c;scanf(“%f,%f”,&a,&b);c=add(a,b);/*函数调用*/printf(sum=%f;c);}求二实数之和(将例1程序改写如下:)第五章函数1.形参与实参调用时:实参值单向传递形参。函数被调用时,临时分配单元给形参,调用完毕,这些单元被释放。实参:出现在调用函数中,形参:出现被调用函数中。五、自定义函数中的几个重要特征实参可为表达式,只传递表达式的值。实参、形参类型一致。可在形参表列中对形参说明。注:2.函数返回或return变量的值;通过return语句将流程返回主调函数。return;函数无返回值return表达式的值;3.函数名要选择一个简洁的有意义的名字作为函数名。从函数名可以反映出函数的功能。六、传值调用和传引用(传址)调用1.传值调用方式:函数的实参和形参的类型均为简单变量。当实参传递形参时系统会建立一份实参的拷贝给形参。当函数调用完毕,这份实参的拷贝消失。特点:传值调用不会影响实参的值。–形参与实参•形式参数:定义函数时函数名后面括号中的变量名•实际参数:调用函数时函数名后面括号中的表达式c=max(a,b);(main函数)(max函数)max(intx,inty){intz;z=xy?x:y;return(z);}例比较两个数并输出大者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);}形参实参2.传引用(传址)调用把实参形参后传递形参变量和实参变量共同内存地址。地址传递:数组名和指针(共用内存单元)特点:形参变量的值的变化会影响实参的值的改变。§5.4函数的存储类型一、标识符的属性一个标识符的属性除了前面已讲过的基本属性外,还具有一些其它的属性:存储类别存储期函数中的变量1、按作用域分局部变量:在本函数内部有效全局变量:从定义位置到源程序结束均有效2、按存储类型分静态存储变量:程序运行时有固定的存储空间,结束时释放内存单元动态存储变量:根据需要动态分配和释放内存单元举例说明二、存储类别1.存储类型和存储期自动型(auto)寄存器型(register)外部型(extern)静态型(static)自动存储期静态存储期2.存储期的性质自动存储:只有变量才具有自动存储期如函数中的变量说明:[auto]floatx,y;[auto]intm,n;作用:进入到函数块中,该变量存在,退出函数块后该变量消失。程序中大部分变量都具有自动存储期。静态存储:可用于变量和函数如:staticint;作用:从程序开始执行起就一直存在,始终占据内存单元,直到程序结束运行。对静态存储变量,若定义时赋初值,则程序运行中仅在第一次调用时赋初值,下一次调用不再赋初值,而是使用上一次调用的值。特别提醒:对静态存储变量,若定义时不赋初值,则系统置初值0,而动态变量不赋初值则值不确定。#includestdio.hintfac(n)/*函数定义*/intn;{staticintf=1;f=fn;return(f);}main(){inti;for(i=1;i=5;i++)printf(%d!=%d\n,i,fac(i));}例:求n!第五章函数1!=12!=23!=64!=245!=120每一次调用fac(i),打印一个i!,同时保留这个i!的值以便下次再乘(i+1)。运行结果为:§5.5标识符的作用域标识符可以具有四种作用域。函数作用域文件作用域块作用域函数原型作用域(作用域:有效范围)可根据标识符的作用域区分局部变量和全局变量。一、局部变量凡在函数(含main函数)内部定义的变量称为局部变量。局部性:局部变量仅在函数内部有效。2.形参为局部变量。3.在复合语句中可定义仅在复合语句中有效的临时变量。1.不同的函数可有同名同类型的变量,它们占不同的内存单元,互不影响。二、全局变量一个源文件中,在所有函数之外定义的变量为全局变量。有效性:自定义位置开始至文件结尾全部有效。例:intp=1,q=5;floatf1(a)inta;{intb,c;}charc1,c2;p,q的作用范围c1,c2的作用范围charf2(x,y);intx,y;{inti,j;}main(){}第五章函数1.全局变量所作用到的函数,相当于这些函数的公共变量。当一个函数对其值进行改变后,另一个函数使用该变量的值亦相应改变。好处:函数之间值传递。2.不要随意使用全局变量。一是始终占据内存单元;二是由于函数依赖于外部定义的变量,减少了通用性。注意3.不在作用域内函数。若使用全局(外部)变量,需在函数体内加上extern保留字。4.全局和局部变量同名时,局部变量有效。局部变