第四章函数4.1综述C是函数型语言,C程序的基本部件是函数.一个函数完成一个特定功能,整个程序的功能靠一系列的函数调用来实现.C程序的执行顺序与函数出现的顺序无关,只与函数的调用顺序有关.C约定无论何时,首先调用的函数是main().C函数分两种:用户自己定义的函数和库函数.使用库函数时应当包含其头文件.2main()f2()f3()f1()f4()f5()4.3函数的定义和调用4.3.1函数定义函数定义的一般形式:函数属性说明函数(值)类型说明函数名(类型标示符参数名1[,类型标示符参数名2,….])如:staticfloatsum(floatx,floaty)/*static、extern*/{说明部分执行部分返回部分[return表达式]}[例]求n!的函数。(exn.c)4.3.2函数的调用C语言规定,当在一个函数中要调用另一个函数时,则必须在调用函数的函数块中对被调函数进行类型的声明。(有例外)1.对被调用的函数的声明方式:[存储类别]数据类型函数名();如:staticintfun();floatarea();2.函数的调用格式调用一个带有参数的函数:函数名(实参表);如:fun(8);调用一个不带参数的函数:函数名();如:print();3.关于函数调用的几点说明(1)函数调用时,实参和形参必须按位置在个数和类型上一一对应;(2)除void类型外的其它类型函数,调用方式如同系统库函数中的数学函数的调用形式一样,可以作为表达式的元素进行调用.(3)整型、实型、字符型函数,用return语句可以返回也只能返回一个函数值,如果希望能从函数返回多个值,则要采用指针、数组等其它传值方式。(4)C语言中如果形参不是指针、不是数组,则实参只能单向传值,即只能是实参将值传给形参,而形参不能将值返回给实参。(5)C语言规定在调用一个函数时,应该对被调函数进行声明。但有三种情况可以不用声明:·如果调用的函数是整型和字符型·如果被调函数是放在主调函数的前面·如果被调函数已经作了全局声明[例]求素数函数。(exprime.c)4.4变量的存储类别和变量的使用范围变量的存储类型规定了该变量的存储区域,而变量的存储区域和变量在程序中的定义位置决定了变量的使用范围。内容:.什么是局部变量和全局变量(变量的作用范围).什么是动态变量和静态变量(变量的存储类别).什么是寄存器变量等有关概念。4.4.1变量的存储类别(auto、static、register、extern)一、auto型:auto型变量在堆栈区域中属于临时性的存储,它并不长期占用内存。因此,C语言程序中大量的变量为auto型的变量,如果某个变量没有指出它的存储类别,则系统默认它为auto型变量。使用auto型变量可以节省内存空间。只有局部变量和形参可以定义为auto类型。二、static型:static型变量是存放在内存的静态存储区域中。这类变量在数据声明时被分配了一定的内存空间,并且这些空间在整个程序的运行过程中,它所占有的空间不释放出来,自始至终都归它使用。其初始化只执行一次。如:staticintt;[例]exstatic.c(比较staticintt=1;与intt=1;的不同)三、register型四、extern型:在函数内部定义的变量,都局限在函数内部使用。定义在函数外部的变量,可以与其它函数或其它文件共享数据。extern语句就是用来对外部变量进行存储方式说明。外部变量提供了不同函数间进行数据通讯的另一种途径。因此当一个程序要用到另一个程序中的某个变量时,就需要将该变量声明为extern类型。4.4.2变量的作用范围变量在程序中的声明位置决定了变量的使用范围。在一个函数内部定义的变量,或在某一对大括号中定义的变量,它的作用范围就在定义的范围之内,脱离了这个范围它们的值就不存在了,这样的变量称为局部变量,也可称为内部变量。与此相对应,有些变量存在于整个程序的运行期间,他们是定义在函数的外部,称全局变量,其特点为能在不同的函数块之间进行数据的传递。具有静态存储的特点。(例求10个数中的最大值exmax.c)4.5函数间的传值一般而言,函数调用过程中先要向被调函数传递要处理的数据(函数参数);被调函数执行完毕,要向主调函数返回处理结果(函数值)。称之为参数传递例如:power_1(x,n);C函数可以有参数,也可无参数;C函数可以有返回值(函数值)也可无返回值,视需要而定。当定义无参数函数时,函数括号中为空,用void显式地标示更好。例如:charstr_cpy();或:charstr_cpy(void);当定义无返回值函数时,用void显式地标示更好。例如:voidshow(inta,floatx);缺省类型说明时,C默认该函数为int型。形式参数和实际参数实际参数(实参)主调函数调用被调函数时提供;形式参数(形参)被调函数为接收传来参数设置。C语言中将数据从一个函数传到另一个函数的方式有三种:(1)用return语句从函数中返回一个函数值;(2)通过形参和实参的结合进行传值;(3)用全局变量在不同函数块间通过数据共享进行传值。一、用return语句从函数中返回一个函数值例:intsum(inta[10]){intsum1=0,I;for(I=0;I10;I++)sum1=sum1+a[I];return(sum1);}二、通过形参和实参的结合进行传值单向值传递:只能将实参的值传给形参,而不能反向传递。main(){inta,b;scanf(“%d%d”,&a,&b);swap(a,b);printf(“a=%d\tb=%d\n”,a,b);}sawp(intx,inty){intt;t=x;x=y;y=t;}35aba实参形参bmain()swap()353t双向传递(地址传值)从子函数返回多个数值,用数组或指针实现。例:用数组#defineN10main(){floata[N]={25,67,45,89,90,75,82,88,65,74},b[2];voidpp();pp(a,b);printf(“ave=%f\n”,b[0]);//b[0]返回的是平均值printf(“max=%f\n”,b[1]);//b[1]返回的是最大值}voidpp(x,y)floaty[],x[];//当虚数组与实数组元素个数相同时{intj;floatmax,ave;max=x[0];//先假设数组的第一个元素是最大值for(ave=0,j=0;jN;j++){ave=ave+x[j];if(maxx[j])max=x[j];}y[0]=ave/N;y[1]=max;}例:用指针main(){inta,b;scanf(“%d%d”,&a,&b);printf(“%d%d\n”,a,b);swap(&a,&b);printf(“%d%d\n”,a,b);}intswap(int*p1,int*p2){int*temp;*temp=*p1;*p1=*p2;*p2=*temp;}实参&a,&b形参*p1,*p22FA0H3120H2FA0H3120H35main()swap()temp353三、用全局变量在不同函数块间通过数据共享进行传值例:exmaxave.c程序中的max变量,它是在main()和pp()函数中全程都可见的全局变量,在pp()子函数中max有了值,则在main()函数中同样使用,所以可以说全局变量将子函数求出的最大值传给了main()函数。这里需要说明的是,程序中全局变量的使用增加了函数块之间的联系,但破坏了函数块的独立性,从而降低了函数块的可移植性。所以在模块化程序设计中一般不太提倡过多地使用全局变量。4.6函数的嵌套调用1.什么是函数的嵌套调用?函数中又调用函数(其它或自身)。例:计算4!+5!+6!+7!(ex413.c)main()调f1()调f2()f1()f2()return或}return或}例:用弦截法求方程的根。设:x3-5x2+16x-80=0方法:1.任取x1、x2,得到f(x1)、f(x2),并使f(x1)*f(x2)0(异号),则在[x1,x2]上必有一根。2.连f(x1)、f(x2)两点,截x轴得x点和f(x)。程序中用下列公式计算x3.若f(x)与f(x1)同号,则根在[x,x2]上,用f(x)作新f(x1),反之亦然。4.若:|f(x)|e则循环2、3两步。(exroot.c)yxxx1x20f(x2)f(x1)f(x)x1*f(x2)-x2*f(x1)f(x2)-f(x1)X=4.7函数的递归调用4.7.1递归函数及其执行特点递归函数又称为自调用函数,即函数允许自己调用自己。如果在一个函数中有一条语句调用了本函数自己,这个函数就是一个递归函数。用递归函数的第一个要点,就是在函数体内必须设置某种控制终止的条件,当条件成立时,终止自调用过程,并使程序的控制逐步地从函数中返回。例:求n!intf(n)intn;{if(n==1)return1;elsereturn(n*f(n-1));}4.7.2函数的递归调用有两种结构直接递归结构:在函数内部有专门调用自身函数的语句(如前求n!)间接递归结构:通过它所调用的函数再来调用自己。4.7.3递归程序设计例:五人说年龄。(exage.c)1)按递推算法age(n)=age(n-1)+2从n=1递推。2)按递归算法age(n-1)+2n110n=1age(3)main()age()n=3c=age(2)+2c=age(1)+2c=10age()age()n=2n=1a(1)=10a(2)=12a(3)=14age(n)=intage(intn);voidmain(){printf(“%d”,age(5));}intage(intn){intc;if(n==1)c=10;/*递归函数中必须有终止嵌套的语句。*/elsec=age(n-1)+2;returnc;}例:用递归算法及欧几里德法求最大公约数(exgcd.c)gcd(b,a%b)b!=0ab=0intgcd(intm,intn);voidmain(){inta,b;scanf(“%d%d”,&a,&b);printf(“gcd=%d”,gcd(a,b));}gcd(a,b)=intgcd(intm,intn){if(n!=0)gcd(n,m%n);elsereturnm;}例:用递归法求Fibonacci数列。(exfib.c)1n=1,2f(n-1)+f(n-2)n2intfeb(intn);voidmain(){intm;scanf(“%d”,&m);printf(“term%dFeb.=%d”,m,feb(m));}intfeb(intn){intf;if(n==1||n==2)f=1;elsef=f(n-1)+f(n-2);returnf;}feb(n)=