第七章函数与模块化程序设计基础7.1函数的定义7.2函数的调用7.3变量的作用域与存储特性7.4模块与函数(选讲)7.1函数的定义从函数使用的角度来看,C语言的函数可以分为两类:标准库函数和用户自定义函数。一.标准库函数与头文件1.TurboC系统提供了400多个标准库函数,按功能可以分为:(1)类型转换函数;(2)字符判别与转换函数;(3)字符串处理函数;(4)标准I/O函数;(5)文件管理函数;(6)数学运算函数等。它们的执行效率高,用户需要时,可在程序中直接进行调用。C语言库函数所用到的常量、外部变量、函数类型和参数说明,都在相应的头文件(扩展名为.h)中声明,这些文件通常存放在系统目录tc\include。如:2.头文件(1)stdio.h文件:标准输入输出函数所用的常量、结构、宏定义、函数的类型、参数的个数与类型的描述。(2)math.h文件:与数学函数有关的常量、结构及相应的函数类型和参数描述。(3)string.h文件:与字符串操作函数有关的常量、结构以及相应的函数类型和参数描述。(4)stdlib.h文件:与存储分配、转换、随机数产生等有关的常量、结构以及相应函数的类型和参数描述。(5)ctype.h文件:字符函数有关的常量、宏定义以及相应函数的类型和参数描述。定义形式二:函数值类型名函数名(形式参数类型及参数说明){数据说明部分语句}定义形式一:函数值类型名函数名(形式参数列表)形式参数类型说明{数据说明部分语句}1.函数定义形式建议使用如:intmax(a,b)inta,b;如:intmax(inta,intb)二.用户自定义函数2.函数由函数说明与函数体两部分构成。(1)函数说明◆函数说明(又称为函数首部):包括函数值类型、函数名、参数类型及参数说明.a.函数值类型指定所定义函数返回值的类型,可以是简单类型、void类型或构造类型等。当函数值类型为void时,表示函数无返回值,相当于其它语言的过程。当函数值类型为int时,可省略其类型的说明,建议不使用缺省形式类型说明.b.函数名是函数的标识符,遵循C语言标识符的命名规则(只能由字母、数字和下划线组成,且以字母或下划线开头),区分大小写。c.形式参数简称形参,处在函数名后的一对圆括号中。要特别注意的是,无论函数是否有形式参数,函数名后的圆括号不可省,并且圆括号之后不能接“;”。d.形式参数属于所在函数的局部变量,其存储类型只能是auto型或register型,缺省为auto型。◆函数体结束在“}”括号处。(2)函数体◆函数说明之后的花括号“{}”部分为函数体。■函数体内数据说明部分在前,执行语句部分在后。◆函数体中说明的变量是该函数调用时有效的局部变量,执行语句是实际生成命令代码的部分。■函数的功能由函数体内的各个语句的执行来实现。}例:定义符号函数sign。sign(x)/*函数返回值类型未说明,默认为int,建议给出函数类型说明*/intx;/*形式参数说明*/{inty;/*函数体局部变量*/y=x0?1:(x==0?0:-1);returny;ifx0return1;ifx==0return0;return–1;等价(3)return语句的表示形式如下:return表达式;或return(表达式);符号函数的另一种定义形式:intsign(intx)/*形式参数类型说明与参数列表与函数名一起说明*/{returny=x0?1:(x==0?0:-1);/*返回一个复杂的表达式的值*/}2。当函数类型为float型并且return语句缺省时,调用出错。注意:1。当函数类型为int型并且return语句的表达式缺省或return语句缺省时,返回不确定的值(TC2中返回值为0)例:程序的运行结果。max()/*函数类型缺省,为int型,return语句缺省*/{}min()/*函数类型缺省,return语句缺省表达式*/{return;}main(){printf(%d,%d\n,max(),min());}程序运行结果为:0,0/*返回值不确定,TC2开发环境中返回值为0*/若函数max与函数min的类型为float,则返回值为:Floatingpointerror:Domain.Abnormalprogramtermination3.函数分类(从函数的形式看)无参函数有参函数◆函数定义时无参数说明◆调用无参函数一般用来执行指定的一组操作◆主调函数不传送数据给被调函数●函数定义时定义了一个或一个以上的参数●调用时将要处理的数据传送给被调函数空函数◆定义时既无参数也无执行语句◆被调用时,不执行任何操作就立即返回◆在具有多个函数的C程序中,主函数出现的位置并不重要。为阅读方便,可将主函数main放在最前面。为了避免过多的函数声明语句,习惯上将主函数放在所有函数之后。不论主函数放在什么位置,一旦启动该程序,总是从主函数开始执行,并且最终在主函数结束整个程序的执行。3.main函数■一个C语言程序至少包含一个函数,并且必须有且只能有一个名为main的函数,称之为主函数。◆在包含多个函数的程序中,不仅可以由主函数调用其它函数,还可以由被调函数调用其它函数,但任何函数都不能调用主函数。■通常主函数的类型定义为void(在TC2中可省略函数main的类型说明)。7.2函数的调用★在C语言的一般函数体中,可以包含对其它函数的调用,称为函数的嵌套调用,甚至包含对自身的调用,称为函数的递归调用。☆一个函数一旦被定义,就可在程序的其它函数中使用它,这个过程称为函数调用。一.函数的一般调用与声明函数名(实际参数列表)1.函数调用形式函数调用的一般形式:◆实际参数也称为实在参数,简称为实参,实际参数之间以“,”分隔。■函数调用时,实参与形参应保持个数、次序及类型的一致性,以确保实参与形参之间数据的正确传递。◆实际参数一般为表达式,可以是常量、变量(调用时必须有确定的值或确定的地址)。■形式参数必须为变量。当实际参数的个数、次序、类型与对应形式参数的个数、次序、类型不一致时,系统并不提示错误,后果却难以预测。注意C语言中函数调用的三种形式:函数语句调用函数表达式调用函数参数调用在函数调用后加“;”,构成一个语句。调用函数的目的可能是执行一个动作或完成特定的功能。大多数函数的调用形式。被调用函数执行的结果为调用函数提供一个值,除非一个函数的类型说明为void型。调用函数通过表达式接收值。被调函数作为某个函数的一个参数。例:函数语句调用与函数表达式调用。#includestdio.hvoidmain(){printf(\nk1=%d,printf(\nk2));}程序运行结果为:k2k1=3intprintf(constchar*format,...);voidmain()/*主函数中采用函数参数调用形式调用函数max2*/{intx,y,z,m;scanf(%d,%d,%d,&x,&y,&z);printf(max=%d\n,max2(max2(x,y),z));/*内层函数max2的值作为外层函数max2的实参,整个函数max2的值又作函数printf的的实参*/}例:函数参数调用形式调用函数max2:intmax2(inta,intb)/*求两个数中较大者*/{inty;y=(ab)?a:b;returny;}2.函数声明◆调用用户自定义函数时,一般调用函数和被调用函数应在同一个文件中,在调用函数中对被调用函数返回值的类型、函数名称、函数形式参数的类型进行说明,这种说明称为函数声明。■函数声明的一般形式如下:函数返回值类型函数名(类型1形参1,类型2形参2,…,类型n形参n);或类型名函数名(类型1,类型2,…,类型n);或类型名函数名();◆函数声明是以语句形式出现的,因此其后有语句结束标记“;”。■若函数定义放在主调函数之前,遵循先定义后调用原则,函数声明可以省略。例:编程求两个整数的阶乘之和。longfac(intn)/*定义函数fac,其功能是求n!*/{inti;longr=1;/*注意r的初始化值为1*/for(i=1;i=n;i++)r=r*i;returnr;}#includestdio.hmain(){longfac(int);/*在主函数中声明后面定义的函数fac*/intm1,m2;longresult;scanf(%d,%d,&m1,&m2);result=fac(m1)+fac(m2);printf(%d!+%d!=%ld\n,m1,m2,result);}二.形参与实参形式参数(简称形参)定义函数使用的参数实际参数(简称实参)调用函数使用的参数◆C语言中,采用函数之间的参数传递方式或用全局变量共享数据方式,使一个函数能对不同的数据进行相同功能的处理。■C语言函数的参数传递均采用单向值传递方式(或称复制方式)单向值传递方式是指在函数调用时,将实参之值传递给对应的形式参数,使形参具有与实参相同的值。■当实际参数是变量的地址值、指针常量或指针变量时,实际参数传递给形式参数的是地址值,也同样是单向值传递方式。例:求方程ax2+bx+c=0(a0)的实数根。分析:(1)一元二次方程的实根的条件是:。(2)定义函数dict来判断数方程是否有实根,有实根则返回函数值1,否则返回函数值0;然后在主函数中求方程的实根。240bac#includestdio.h#includemath.hmain(){floata,b,c,x1,x2,d,dt;intdict(float,float,float);/*声明函数dict及形式参数类型*/printf(Inputa,b,c:);scanf(%f,%f,%f,&a,&b,&c);/*输入方程*/d=dict(a,b,c);/*调用函数dict,传递实参a,b,c*/dt=b*b-4*a*c;/*,可以用一个函数实现*/24bacif(d){x1=(-b+sqrt(dt))/(2*a);x2=(-b-sqrt(dt))/(2*a);printf(实根x1=%f,x2=%f\n,x1,x2);}elseprintf(无实数根!\n);}intdict(floata1,floatb1,floatc1)/*定义函数dict及形参说明*/{floatd;d1=b1*b1-4*a1*c1;/*可以用一个函数实现*/if(d1=0)return(1);elsereturn(0);}调用函数main被调用函数dicta1b1c1abc实参与形参数据传递通过函数调用,将实参的值传递给形参.函数调用时,系统对参数的处理步骤为:(1)计算各实参的值,将实参值压入形参栈中,然后执行函数体。(2)当函数执行完返回时,形参从栈顶弹出(取走)。有关实参与形参的几点说明:(1)在函数定义中指定的形参,未调用时,它们不占用存储单元只有调用该函数时,形参才被分配空间,函数调用结束后,形参所占的存储单元被释放。(2)实参为表达式。函数调用时,先计算表达式的值,然后将值传递给形参。常量、变量、函数值都可看成是表达式的特殊形式。(3)定义函数时,形参的排列没有次序要求,但对形参列表中每个参数要进行说明。调用函数时,实参类型、个数及排列次序应与形参一一对应。若类型不一致,必须在参数前加上强制转换符,否则会发生“类型不匹配”的错误。(4)实参与形参的数据传递为单向传递,只可由实参向形参传递,不能由形参传回实参。实参与形参处在不同的函数中,作用的区域不同,即使实参与形参同名,也是不同的变量。三.函数的嵌套调用所谓函数的嵌套调用是指一个函数调用另一函数的过程中又出现对其它的函数调用。这种嵌套调用的层次原则上不限制。函数1函数2函数3{……①②{…….③④{…...⑤调用函数2调用函数3}……⑨⑧…….⑦⑥}}例:采用函数多重嵌套调用求方程ax2+bx+c=0(a0)的实数根。#includestdio.h#includemath.hmain(){floata,b,c,x1,x2;intdict(float