第4章函数4.1函数的定义与声明4.2函数的调用4.3变量的存储类别4.4内部函数和外部函数4.5编译预处理本章小结习题四4.1函数的定义与声明4.1.1函数的概念通过前面的学习,我们知道C语言源程序是由函数组成的。函数是构造C程序的基本模块,它相当于其他高级语言中的子程序。实际上,C语言源程序可以由一个主函数和若干个其他函数组成。其他函数可以是标准的库函数,也可以是用户自己编写的函数。每个函数具有完整的、独立的功能,从而使程序整体呈现出清晰的结构。可以说在解决实际问题时,无论涉及的问题是复杂还是简单,规模是大还是小,用C语言编写程序,其任务都只有一个,那就是编写函数,至少要编写一个main()函数。可以说C程序的功能就是由各式各样的函数实现的,即函数模块=功能模块C语言中“函数”的概念与数学上“函数”的概念有相似之处,但又不完全相同。在英语中“函数”与“功能”是同一个单词,即function。所以,从理解的角度,与其说“函数模块”,倒不如说“功能模块”更恰当,或者说“模块”更简单。C程序通过对函数模块的调用可实现特定的功能。可以把函数看成一个“黑匣子”,只要将数据送进去就能得到结果,而函数内部究竟如何工作,外部程序是不知道的。外部程序所知道的仅限于输入给函数什么以及函数会输出什么。从定义的角度看,函数可以分为系统库函数和用户自定义函数。(1)系统库函数。系统库函数也叫标准库函数,简称标准函数。为了用户使用方便,每一种版本的C编译系统都提供一批由商家开发编写的函数,放在一个库中,这就是函数库。函数库中的函数称为库函数,或系统库函数。使用系统库函数时,用户无须定义,也不必在程序中声明类型,只要在主程序前用编译预处理命令将有关该函数原型的头文件包括到程序中即可。ANSIC和TurboC2.0均提供了300多种库函数,这些库函数从功能上可分为以下几种:1)字符分类函数:用于对字符按ASCII码分类(分为字母、数字、控制字符、分隔符、大小写字母等)。2)转换函数:用于字符或字符串的转换(在字符量和各类数字量(整型、实型等)之间进行转换;在大、小写字母之间进行转换)。3)目录路径函数:用于文件目录和路径操作。4)诊断函数:用于内部错误检测。5)图形函数:用于屏幕管理和各种图形输出。6)输入/输出函数:用于完成输入/输出。7)接口函数:用于与DOS,BIOS和硬件的接口。8)字符串函数:用于字符串操作和处理。9)内存管理函数:用于内存管理。10)数学函数:用于数学函数计算。11)日期和时间函数:用于日期、时间进行转换操作。12)进程控制函数:用于进程管理和控制。13)其他函数:用于其他各种功能。以上各类函数不仅数量多,而且有的还需要硬件知识才会使用,因此想要全部掌握需要一个较长的学习实践过程。应首先掌握一些最基本、最常用的函数,再逐步深入。(2)用户自定义函数。库函数完成的功能是有限的,要解决用户的专门问题,就需要自己编写所需的函数,即由用户自己定义的函数,简称自定义函数。自定义函数时不仅要在程序中定义函数模块本身,而且还要在主调函数模块中对该被调函数进行类型声明,然后才能使用,即用户把自己的算法编成一个个相对独立的函数模块,然后通过调用的方法来使用自己的函数。从函数的形式看,函数又可以分为无参函数和有参函数。(1)无参函数,即函数中没有任何参数。在调用无参函数时,主调函数并不将数据传送给被调函数。无参函数可以返回或不返回函数值,但一般以不返回函数值为多。(2)有参函数,即函数中包括相应的参数。在调用有参函数时,在主调函数和被调函数之间存在着数据的传递关系。也就是说,主调函数可以将数据传给被调函数使用,被调函数也可以回送数据给主调函数使用。一个C源程序文件一般由一个或多个函数组成。一个大的项目程序一般由一个或多个源程序文件组成,一个源程序文件是一个独立的编译单位。4.1.2函数的定义一般来说,函数只定义不声明不能使用,无定义有声明则属非法。定义函数是在程序中编写一条块分割、相对独立、功能单一、接口简单、调用容易、有返回结果的模块。函数定义的一般形式如下:类型标识符函数名(形式参数表列){函数体}其中:“类型标识符函数名(形式参数表列)”称为函数首部(functionheader),“类型标识符”必须是C语言合法的关键字,“函数名”可以是用户自定义的标识符,“形式参数表列”是用逗号隔开的若干临时变量。“函数体”(functionbody)是一个复合语句,一般由说明部分和语句部分组成。【例4.1】输入3个整数,求出其中最大值。程序:程序运行情况:屏幕提示:Inputthedatas:输入数据:652431↙输出结果:Maxis65分析:自定义函数max()的功能是求两个数中的较大数。main()两次调用自定义函数max(),求出3个数中的最大数。main()第一次调用自定义函数max()时,得到的返回值是a和b中的较大数,并赋值给变量t,第二次调用自定义函数max()时,将变量t的值作为实际参数,也就是将t的值与c的值进行比较,最后返回的值就是a,b,c中的最大值。【说明】(1)函数定义的一般形式由“函数首部”(函数头)和“函数体”两部分组成。“函数首部”即函数定义的第一行,包括函数的类型、函数名和形式参数表列。“函数体”即花括号“{}”中的部分,在语法上可以是一条语句,也可以是一个复合语句。函数的定义必须独立并先于函数的声明和调用。(2)类型标识符定义了函数返回值的类型。函数在调用结束后一般可以返回一个值,该返回值由函数的类型决定。当函数的类型缺省时,系统默认为int型。当函数类型标识符为void时,函数没有返回值,一般称为“空类型函数”。(3)函数名是用户为函数定义的名字,必须符合C语言标识符的命名规则。(4)形式参数简称形参,形式参数表列简称形参表列。形参表列中的形参用于接收从主调函数传来的实参数据。(5)当没有形参时,函数名后面的一对圆括号不能省略。(6)花括号“{}”中的“说明部分”和“语句部分”构成了函数体。一般情况下函数体中定义的变量,仅在函数执行期间临时存在。函数体中也可以没有变量定义,只有语句,或者二者都没有。例如:voidnull(void){}(7)C语言的所有函数都是并列的,即函数定义是互相独立的。一个函数并不属于另一个函数。任何一个函数都不能定义在另一个函数的内部,即函数不能嵌套定义。4.1.3函数的声明通常在主调函数中,要对将要调用的函数事先声明。声明的目的是通知编译系统,在本函数中将要调用哪些函数以及它们的信息(函数名、函数的类型、形参的个数和类型以及次序等),以便编译系统对函数进行检查。例如,形参与实参的类型、个数、次序是否一致,调用函数返回值的类型是否正确等。函数声明的一般形式如下:类型标识符函数名(形式参数表列);例如:doublepower(intx,inty);以上的函数声明也称为函数的原型。形式参数表列中形参的数目和类型是至关重要的,而形参的名字则可以省略。例如:doublepower(int,int);power()函数的类型是double类型,它有两个形参,其类型是int型。函数声明可位于主调函数的函数体内或函数体外(一般位于程序开头部分)。在函数体外声明的函数可在声明之后,直到该文件结束时被任何函数调用;在函数体内声明的函数只能在该函数体内被调用。有时,函数也可以缺省声明。缺省声明有以下几种情况:(1)被调用函数写在主调函数之前。(2)被调用函数的类型是int型。(3)调用系统库函数时,不需对其声明,但必须用#include预处理命令包含所需的头文件。对所有被调用函数均进行声明是好的编程习惯,这既符合现代程序设计的风格,又方便程序的阅读和检查。4.1.4函数参数和函数的返回值1.形式参数和实际参数多数情况下,主调函数和被调函数之间都有数据传递关系,而参数就是函数调用时传递数据的载体。我们把在定义函数时,函数名后面圆括号中的变量,称为“形式参数”(formalparameter);在调用函数时,函数名后面圆括号中的变量,称为“实际参数”(acrualparameter)。【例4.2】输入k和r值,计算k!/(r!(k-r)!)的值。程序:main(){intr,k;floata,b,c,d;printf(n0,dataerror);t=-1;}elsefor(i=2;i=n;i++)t=t*i;return(t);}程序运行情况:屏幕提示:Pleaseinputr,k:输入数据:2↙6↙输出结果:k!/(r!(k-r)!)=15.00【说明】(1)形参变量只有当函数被调用时才分配内存单元,调用后自动释放。它有两个作用:一是表示从主调函数接收哪些类型的信息;二是表示在函数体中有哪些参与运算或被输入和输出。(2)实参可以是常量、变量或表达式,但必须有一个确定的值。(3)实参和形参必须在个数上相等,类型上兼容。(4)实参和形参之间的值传递,实际上是赋值操作,即把实参之值赋给形参。2.函数的返回值函数的返回是指流程从被调函数返回到主调函数。有的函数有返回值,有的函数没有返回值。通常该返回值是通过return语句来实现的。return语句的一般形式如下:return表达式;或return(表达式);或return;其中,return是关键字,“表达式”是需要返回给主调函数的值。return语句有两个作用:一是宣告一次函数调用的结束;二是带回函数的返回值,送到调用表达式中去。【说明】(1)return语句中表达式值的类型应与函数的类型一致,当不一致时,返回值的类型取决于函数的类型。函数默认的返回值类型为int型。(2)在一个函数中可以有一个或多个return语句,但最终只能执行一个。(3)函数调用不需要返回值时,return后可不跟表达式,也可以没有return语句。没有该语句时,函数体的最后一个花括号“}”的作用是将流程返回主调函数。(4)有时为了明确表示函数无返回值,可以将函数定义为“void”类型。但应注意:一旦函数定义为“void”类型,就不能再使用被调用函数的返回值。例如:如果已将fun(x,y)函数定义为void类型,则下面的语句是错误的。c=fun(a,b);编译时,系统会给出错误的信息。4.2函数的调用C语言源程序由一个主函数和若干个自定义函数组成,因此,C程序是一系列被定义函数的集合。一个函数被另一个函数调用的过程称为函数的调用。一个函数可以被主函数main()调用,也可以被其他函数调用,各函数之间也可以相互调用,但不能调用主函数main()。习惯上人们常常把调用函数称为主调函数(main()函数一定是主调函数),把被调用函数称为被调函数(系统库函数一定是被调用函数)。4.2.1函数调用的一般形式所谓函数调用(functioncall),是指使程序流程转向所定义的函数。函数一旦定义,以其名为首地址的一段内存单元将被该函数占有。因此当在程序中出现该函数名时,就意味着程序将转到这一段内存地址,调用该函数,执行相关的一系列操作。对于有参函数,通常情况下,函数的调用是靠形参和实参的“虚实结合”来完成“值传递”的。1.函数调用的一般形式函数调用的一般形式如下:函数名(实际参数表列)其中,“函数名”必须与函数定义、函数声明时的函数名同名;“实际参数表列”可以是若干个有确定值的变量或表达式等,若有多个实参,则各实参之间必须用逗号隔开。【说明】(1)实际参数简称实参,实际参数表列简称实参表列。在实参表列中,实参可以是变量名、常量、表达式或者是我们后面将要学到的数组名、数组元素、结构体变量名或指针等。(2)无参函数调用的一般形式为:函数名()。函数名后面的括号不能省略。(3)被调函数总有“何处调用,返回何处”的特点。2.函数调用的条件在一个函数中调用另一函数(即被调用函数)必须具备以下条件:(1)被调用(声明)函数必须是已经存在的函数(包括库函数或自定义函数)。(2)如果使用库函数,则一般应该在本文件开头用#include命令将调用有关库函数时所需的信息“包含”到本文件中来