1、第 7章 函 数 第第 7章章 函函 数数 7.1 函数的定义函数的定义 7.2 函数的调用函数的调用 7.3 数据传递方法数据传递方法7.4 嵌套调用和递归调用嵌套调用和递归调用 7.5 变量的作用域变量的作用域 7.6 变量的存储类别变量的存储类别 7.7 结构化程序设计方法结构化程序设计方法 第 7章 函 数 7.1 函数的定义函数的定义 7.1.1 函数的结构函数的结构 1函数头 函数头有经典与现代两种形式,现代形式称为函数原型。经典形式:函数属性 函数类型 函数名(函数参数表)参数说明:例如:static float f1(a,b,c)/*函数特征说明*/float a,b,c;/*
2、参数说明*/第 7章 函 数 函数属性为static,函数类型为float,函数名为f1,函数参数表为“a,b,c”。现代形式:函数属性 函数类型 函数名(参数说明)例如:static double f2(float x,float y,float z)第 7章 函 数 说明:(1)函数的属性或称函数的存储属性包括内部函数与外部函数两种。内部函数用保留字static描述,又称静态函数。外部函数用保留字extern标识。缺省函数的属性默认为外部函数。外部函数可以供构成程序的所有文件中的函数调用,内部函数只有同一程序文件中的函数才能调用。(2)函数类型给出函数返回值的数据类型,缺省时默认为int型
3、。当函数没有返回值时,函数为无类型,标准C语言中用保留字void标识无类型(或称空类型)。此时函数相当于其它高级语言中的过程子程序。第 7章 函 数 (3)函数名可以是任何合法的标识符,是程序设计人员为函数取的名字。函数的名字实际上表示的是函数被调用时的入口地址。(4)函数中的参数称为形式参数(简称形参),参数的主要作用是在函数被调用时实现主调函数与被调函数之间的数据传递,同时参数在相应函数体内可同普通变量一样使用。注意:在现代定义形式中,每个参数必须分别指明其数据类型,即使是相同类型的参数也必须分开说明。第 7章 函 数 (5)函数可以无参数,此时称为无参函数。主函数也可以有参数,这将在第1
4、0章中加以介绍。(6)鼓励读者采用函数原型的现代形式。本书前两部分还是采用传统形式,第三部分采用现代形式。第 7章 函 数 2函数体 函数体由服务于函数的数据说明与执行语句两部分组成。函数的数据说明包括数据定义和数据申明,用以完成数据描述。函数的执行语句完成函数的操作描述。Turbo C中的数据说明必须位于函数体或复合语句的开始处,但在C+中可根据需要随时描述数据。一个函数的函数体可以为空,此时的函数称为空函数。空函数不产生任何操作,常用于程序调试。空函数的形式为:void f()第 7章 函 数 7.1.2 返回语句返回语句 通过在函数中使用返回语句,返回一个值给函数,同时终止函数的调用,返
5、回主函数。格式:return(表达式);或return 表达式;功能:(1)计算表达式的值,将表达式的值返回给函数。(2)从被调用的函数返回主调函数。第 7章 函 数 说明:(1)返回值类型应和函数类型一致,不一致时返回值将自动转换成函数类型。(2)函数中可以有多条返回语句,这时一般与if语句联用,执行到哪一条返回语句,哪一条返回语句就起作用。(3)函数中无返回语句,则执行至函数体结尾时返回。此时将返回一个不确定的值给函数。(4)如果只需要从函数中返回,而不需带回值,那么可使用不带表达式的return语句和void类型函数。第 7章 函 数 7.1.3 函数的定义函数的定义(1)需定义函数的属
6、性。(2)需定义函数的类型。(3)给函数取一个名字。(4)设计函数的参数。(5)对函数中使用的量进行定义。(6)对函数的执行部分进行描述。第 7章 函 数 例例 7 1 求两个数的最大值函数。/*求两个数的最大值函数*/float max(x,y)(1)函数值类型float float x,y;(2)函数名maxfloat m;(3)函数参数x、y,类型为float if (xy)m=x;else m=y;return(m);/*返回最大值*/第 7章 函 数 说明说明:(1)函数头可以采用现代形式:float max(float x,float y)(2)函数体也有多种等价形式:形式一:if
7、 (xy)return(x);else return(y);形式二:return(xy?x:y);第 7章 函 数 例例 7-2 判断两个数是否是相等函数。方法一方法一:/*判断两个数是否是相等函数*/int eq(x,y)(1)函数值类型int,可以缺省float x,y;(2)函数名eqfloat t;(3)函数参数x、y,类型为float if (x=y)t=1;else t=0;return(t);/*相等返回1,不等返回0*/第 7章 函 数 方法二方法二:/*判断两个数是否是相等函数*/int eq(x,y)float x,y;if (x=y)return(1);else retu
8、rn(0);第 7章 函 数 方法三方法三:/*判断两个数是否是相等函数*/int eq(x,y)float x,y;return(x=y);第 7章 函 数 例例 7-3 符号函数。方法一:/*符号函数*/char sign(x)/*函数值类型为char*/float x;/*函数名为sign*/char s;/*函数参数为x,类型为float*/if (x=0)s=+;else s=;return(s);第 7章 函 数 方法二:方法二:/*符号函数*/char sign(x)float x;if (x=0)return(+);else return();第 7章 函 数 例7-4 平方函
9、数。/*平方函数*/float sq(x)/*函数值类型为float*/float /*函数名为sq*/return(x*x);/*函数参数为x,类型为float*/第 7章 函 数 例7-5 素数判断函数。/*素数判断函数*/int pn(n)/*函数值类型为int*/int n /*函数名为pn*/int i;/*函数参数为n,类型为int*/int flag;/*素数标志*/flag=1;for(i=2;i=n/2;i+)if(n%i=0)flag=0;break;return(flag);第 7章 函 数 例例7-6 阶乘函数。阶乘函数。/*阶乘函数*/long int fn(n)/*
10、函数值类型为long int*/int n;/*函数名为fn*/int i;/*函数参数为n,类型为int*/long int t;t=1;for(i=1;iz)&(y+zx)&(z+xy)printf(三边为%5.2f,%5.2f,%5.2f的三角形面积等于%5.2f n,sf(x,y,z);else printf(不能构成三角形!n);/*求第二个三角形的面积*/x=y=z=1;if(x+yz)&(y+zx)&(z+xy)printf(三边为%5.2f,%5.2f,%5.2f的三角形面积等于%5.2f n,sf(x,y,z);else printf(不能构成三角形!n);第 7章 函 数
11、运行结果:三边为3.00,4.00,5.00f的三角形面积等于6.00 三边为1.00,1.00,1.00f的三角形面积等于0.43 第 7章 函 数 例例7-9 调用函数求21000之间的所有素数。算法提示:(1)对21000之间的某个数,调用素数判断函数来判断这个数是否为素数。(2)对21000之间所有数的判断处理,可用for循环999次调用素数判断函数来完成。(3)素数判断函数参见例7-5。第 7章 函 数 程序如下:/*程序 7-9,调用函数求21000之间的所有素数*/int pf(n)/*素数判断函数*/int n;int i;int flag;flag=1;for(i=2;i=n
12、/2;i+)if (n%i=0)flag=0;break;return(flag);main()/*主函数*/第 7章 函 数 int i;int count=0;/*统计素数个数,用以控制输出格式*/clrscr();printf(21000之间的素数如下:n);for(i=2;i=1000;i+)if(pf(i)=1)printf(%6d,i);count+;if(count%5=0)printf(n);/*每行输出5个素数*/第 7章 函 数 7.2.3 被调函数说明被调函数说明 用户自定义函数一般需在调用前在主调函数中进行说明。函数说明是一种申明,是告诉主调函数这里调用的是一个什么样的
13、函数。函数说明的一般形式为:被调函数类型 被调函数名();注意,不是函数定义,其后有分号。第 7章 函 数 例例 7-10 求2n之间的所有素数。main()int n;int i;int count=0;int pf();/*函数说明*/clrscr();printf(请输入n:);scanf(%d,&n);printf(2n之间的素数如下:n);for(i=2;it)t=y;if(zt)t=z;return(t);第 7章 函 数 (2)传地址方式将对应实参地址传递给相应形参,实参与形参共享存储单元。这时,一方面可完成批量数据的传递,另一方面形参的改变将引起对应实参的改变,实现数据的双向传
14、递,并将多个数据带回(这在PASCAL语言中用变参实现)。传地址方式增加了函数之间的联系,有副作用,不利于将程序中的错误隔离。第 7章 函 数 7.4 嵌套调用和递归调用嵌套调用和递归调用7.4.1 嵌套调用嵌套调用 第 7章 函 数 例7-11 求2n之间的完数,利用函数的嵌套调用完成。算法提示:(1)定义一个函数,判断某数是否为完数。(2)定义另一个函数,求2n之间的完数,调用完数判断函数完成。第 7章 函 数 程序如下:main()/*请读者给出*/*求2n之间的完数的函数,调用完数判断函数完成*/void wsh(n)int n;int i;int wf();/*函数说明*/print
15、f(2%d之间的完数如下:n,n);for(i=2;i=n;i+)if(wf(i)=1)printf(%6d,i);第 7章 函 数/*判断x是否为完数的函数*/int wf(x)int x;int i,s=0;int w;for(i=1;ix;i+)if(x%i=0)s+=i;if(s=x)w=1;else w=0;return(w);函数调用过程:主函数wsh(n)wf(i)。第 7章 函 数.)!(!nmnmCnm例7-12 计算 的值。算法提示:(1)定义求阶乘函数,在此基础上定义求组合数函数。(2)主函数调用求组合数函数,求组合数函数三次调用求阶乘函数。程序如下:main()/*请读
16、者给出*/*求阶乘函数*/long int jf(n)int n;int i;第 7章 函 数 long int t=1;for(i=1;i0)j=n*jf(n1);/*递归调用*/else j=1;/*递归终止*/return(j);第 7章 函 数 我们也可先判断是否为递归调用终止条件,如不是,按转化规则转化来写递归函数。long int jf(n)I /*求阶乘的递归函数二*/int n;long int j;if(n=0)j=1;/*递归终止*/else j=n*jf(n-1);/*递归调用*/return(j);第 7章 函 数 如下是计算4!的过程:if(4)=4*jf(3)if(
17、4)=24 if(3)=3*jf(2)if(3)=6 if(2)=2*jf(1)if(2)=2 if(1)=1*jf(0)if(1)=1 if(0)=1 第 7章 函 数 递归调用分成两个阶段完成。第一阶段是递进阶段(所指方向),即递归的调用阶段,完成转化阶段的处理。第二阶段是回归阶段(所指方向),也就是递归的返回阶段,即由特定问题的解递推出所求问题的解。从一般意义上讲,这也就是嵌套调用的逐级调用,逐层返回。不过,这里调用的是函数自身,而且一般调用的次数比较多。递归调用的实现要用到堆栈,用堆栈来保留调用现场。递归调用实际上是多重嵌套调用的一种特殊情况。递归调用的次数称为递归的深度。第 7章 函
18、 数 例例7-13 计算mn 的递归函数。方法一:转化:mn mn1mn2m2 m1m0。转化的规则是:mn=m*mn1。终止:n=0,m0=1。相应的递归函数为:long int mn(m,n)/*计算m 的n次方的递归函数*/int m,n;long int j;if(n0)j=m*mn(m,n1);/*递归调用*/else j=1;/*递归终止*/return(j);第 7章 函 数 方法二:方法二:转化:n为偶数,mnmn/2m0,规则为mn=(mn/2)2;n为奇数,mn m(n1)/2m0,规则为mn=m*(m(n1)/2)2。终止:n=0,m0=1。相应的递归函数请读者自己定义。
19、采用方法二,递归调用的次数比方法一要少,效率要高。第 7章 函 数 例例 7-14 用递归方法计算Fibonacci数列的第n项函数。转化:n2,f(n)=f(n-1)+f(n-2)终止:n=1、2,f(1)=f(2)=1相应的递归函数为:int fib(n)/*计算Fibonacci数列的第n项*/int n;int j;if(n2)j=f(n-1)+f(n-2);/*递归调用*/else j=1;/*递归终止*/return(j);第 7章 函 数 例例 7-15 年龄问题。有5个人坐在一起,问第5个人多少岁,他说比第 4 个人大2岁。问第4个人多少岁,他说比第3个人大2岁。问第3个人多少
20、岁,又说比第2个人大2岁。问第2个人多少岁,说比第1个人大2岁。最后问第1个人多少岁,他说是10岁。请问第5个人多少岁?。转化:n1,age(n)=age(n-1)+2 终止:n=1,age(1)=10 第 7章 函 数 age(n)/*求年龄的递归函数*/int n;int a;/*用作存放函数的返回值*/if(n=1)a=10;else a=age(n-1)+2;return(a);main()/*主函数*/printf(第5个人的年龄=%dn,age(5);运行结果:第5个人的年龄=18 第 7章 函 数 7.5 变量的作用域变量的作用域 7.5.1 局部变量局部变量 (1)在一个函数内
21、部定义的变量是局部变量,只能在函数内部使用。(2)在主函数内部定义的变量也是局部变量,其它函数也不能使用主函数中的变量。(3)形式参数是局部变量。(4)在复合语句中定义的变量是局部于复合语句的变量,只能在复合语句块中使用。(5)局部变量在函数被调用的过程中占有存储单元。(6)不同函数中可以使用同名变量。第 7章 函 数 例如,有如下程序:main()int a=1;int b=2;/*a,b为局部变量*/int b=1;/*此b非前b,语句块中局部变量*/printf(复合语句中a=%d,b=%dn,a,b);printf(复合语句外a=%d,b=%dn,a,b);运行结果:复合语句中a=1,
22、b=1 复合语句外a=1,b=2 第 7章 函 数 7.5.2 全局变量全局变量 (1)在函数外部定义的变量是全局变量,其作用域是变量定义位置至整个程序文件结束。(2)使用全局变量,可增加函数间数据联系的渠道。全局变量可以将数据带入在作用域范围内的函数,也可以将数据带回在作用域范围内的其它函数。第 7章 函 数 int a,b;/*a,b为全局变量*/int s()a=a*a;/*函数中a、b即为前定义的全局变量*/b=b*b;return(a+b);main()int sum;a=2;b=3;sum=s();printf(sum=%d,a=%d,b=%d,sum,a,b);运行结果:sum=
23、13,a=4,b=9 第 7章 函 数 当然,这也是一个递推问题,我们可直接用循环来处理。使用递归算法层次分明,形式简练,可读性强,符合人们的思维习惯。对一些能够用递归方法编程的问题,尽量使用递归。但使用递归方法编写的程序执行时的时间、空间开销比较大,这也要求慎用递归。此时可改用迭代的方法或递推的方法,用循环实现。第 7章 函 数(3)提前引用外部变量,需对外部变量进行说明,或称申明。形式:extern 类型 变量表;作用:说明这些变量是已在外部定义过的变量。例如,有如下程序:main()extern int a,b;/*外部变量说明*/int a,b;/*外部变量定义*/第 7章 函 数 说
24、明:这样还不如将外部变量提前定义,先定义、后使用。变量的定义与说明同前面介绍过的函数的定义与说明类似,变量不能重复定义,但可以多次说明。第 7章 函 数 (4)使用程序中非本程序文件的外部变量,也要对使用的外部变量进行同上的申明,或用文件包含处理。(5)局部变量如与外部变量同名,则在局部变量的作用域内,外部变量存在,但不可见,外部变量的作用被屏蔽。(6)全局变量在程序运行过程中均占用存储单元。(7)在编程时,原则上尽量少用全局变量;能用局部变量,不用全局变量,要避免局部变量全局化。第 7章 函 数 7.6 变量的存储类别变量的存储类别 7.6.1 变量的存储类别变量的存储类别 内存中供用户使用
25、的存储空间分为代码区与数据区两个部分。变量存储在数据区,数据区又可分为静态存储区与动态存储区。静态存储是指在程序运行期间给变量分配固定存储空间的方式。如全局变量存放在静态存储区中,程序运行时分配空间,程序运行完释放。第 7章 函 数 动态存储是指在程序运行时根据实际需要动态分配存储空间的方式。如形式参数存放在动态存储区中,在函数调用时分配空间,调用完成释放。对于静态存储方式的变量可在编译时初始化,默认初值为0或空字符。对动态存储方式的变量如不赋初值,则它的值是一个不确定的值。在C语言中,具体的存储类别有自动(auto)、寄存器(register)、静态(static)及外部(extern)四种
26、。静态存储类别与外部存储类别变量存放在静态存储区,自动存储类别变量存放在动态存储区,寄存器存储类别直接送寄存器。第 7章 函 数 变量存储类别定义方法:存储类别 类型 变量表;例如:(1)a,b,c为整型自动存储类别变量:auto int a,b,c;(2)x,y,z为双精度型静态存储类别变量:static double x,y,z;第 7章 函 数 7.6.2 局部变量的存储方式局部变量的存储方式 (1)局部变量一般用自动方式存储,用保留字auto加以定义,此时称为自动变量,是动态存储,在函数的调用过程中存在,由编译系统自动处理。例如:void f()auto int i,j;auto fl
27、oat x,y;/*局部变量i,j,x,y以自动方式存储*/第 7章 函 数 (2)如果希望函数调用完后局部变量的值被保留,不释放其所占存储单元,这时必须将其存储方式定义为静态存储方式,用保留字static加以定义。用静态方式存储的局部变量称为局部静态变量,在函数调用完后其值被保留,由于“局部”的作用域,这个保留的值其它函数并不能使用,只有本函数能够使用。第 7章 函 数 例例 7-16 计算1到5的阶乘。int jf(n)int n;static int f=1;f=f*n;return(f);main()int i;for(i=1;i=5;i+)printf(%d!=%dn,i,jf(i)
28、;运行结果为:1!=12!=23!=64!=245!=120 第 7章 函 数 每次调用函数jf(i)时,计算出i!,同时该值被保留,可供下次调用函数求下一个阶乘使用,这个保留的值其它函数不能使用。如果函数jf()中变量f不定义为局部静态变量,则每次调用都被初始化为1,运行结果为:1!=12!=23!=34!=45!=5 第 7章 函 数 (3)如果对数据的读写频繁且存取速度要求较快,C语言允许用寄存器方式直接处理变量,即用保留字register对变量加以描述,此时该变量称为寄存器变量。例如:long sum(n)int n;int i;register long s=0;/*s定义为寄存器变
29、量*/for(i=1;i=n;i+)s+=i;return(s);第 7章 函 数(4)局部变量的缺省存储方式是自动存储方式。第 7章 函 数 7.6.3 全局变量的存储方式全局变量的存储方式 (1)全局变量一般用外部存储方式存储,用保留字extern加以定义。此时,变量的作用域是构成整个程序的所有程序文件,也就是定义的外部变量可供其它程序文件使用。例如,程序由两个程序文件file1.c与file2.c组成。/*file1.c*/extern int a;/*定义extern存储方式变量a*/main()int pow();int n;int p;第 7章 函 数 scanf(%d,&n);p
30、=pow(n);printf(p=%dn,p);/*file2.c*/extern int a;/*申明本文件中使用的是已定义的外部变量a*/int pow(x)int x;int i,t=1;for(i=1;i0)rf5(a,b,c);/*调用函数rf5()解方程a*x*x+b*x+c=0,d0*/else rf 6(a,b,c);/*调用函数rf6()解方程a*x*x+b*x+c=0,d0*/float a,b,c;float x1,x2;x1=(b+sqrt(d)/(2*a);x2=(bsqrt(d)/(2*a);printf(方程第一个根=%6.2f,第二个根=%6.2f n,x1,x
31、2);第 7章 函 数 void rf6(a,b,c)/*解方程a*x*x+b*x+c=0,dc)&(b+ca)&(c+ab);if(flag)l=0.5*(a+b+c);s=sqrt(l*(la)*(lb)*(1c);printf(所求三角形面积=%7.3fn,s);else printf(不能构成三角形!n);第 7章 函 数 void ts()/*计算梯形面积*/float a,b,h;float s;printf(请输入梯形的上底,下底,高:);scanf(%f,%f,%f,&a,&b,&h);s=(a+b)*h*0.5;printf(所求梯形面积=%7.3fn,s);第 7章 函 数
32、 void ps()/*计算平行四边形面积*/float a,h;float s;printf(请输入平行四边形的底和高:);scanf(%f,%f,&a,&h);s=a*h;printf(所求平行四边形面积=%7.3fn,s);void tc()/*退出程序*/printf(退出面积计算程序!);exit(0);第 7章 函 数 例7-20 找出1001000之间的素数、完数、回文数、水仙花数等特殊数。算法提示:(1)定义四个函数ss()、ws()、hs()、shs()求出相应的特殊数。(2)主程序仅包含函数ss()、ws()、hs()、shs()的调用。整个程序结构清晰,可读性极好。第 7
33、章 函 数 程序如下:/*程序7-20,计算1001000之间的特殊数*/main()/*主程序*/void ss();/*函数申明*/void ws();void hs();void shs();printf(计算1001000之间的特殊数n);ss();/*调用函数ss()、ws()、hs()、shs()求出相应特殊数*/ws();hs();shs();第 7章 函 数 void ss()/*计算1001000之间的素数*/int i,j;int p;printf(1001000之间素数如下:n);for(i=100;i=1000;i+)for(p=1,j=2;j=i;j+)if(i%j=
34、0)p=0;break;if(p)printf(%8d,i);printf(n);第 7章 函 数 void ws()/*找出1001000之间的完数*/int i,j;int s;printf(1001000之间的完数为:n);for(i=100;i=1000;i+)for(s=0,j=1;ji;j+)if(i%j=0)s+=j;break;if(s=i)printf(%8d,i);printf(n);第 7章 函 数 void hs()/*找出1001000之间的回文数*/int i;printf(1001000之间的回文数为:n);for(i=100;i 1000;i+)if(i%10=
35、i/100)printf(%8d,i);printf(n);第 7章 函 数 void shs()/*找出1001000之间的水仙花数*/int i,i1,i2,i3;printf(1001000之间的水仙花数为:n);for(i=100;i1000;i+)i1=(i%10)*(i%10);i2=(i/10%10)*(i/10%10)*(i/10%10);i3=(i/100)*(i/100)*(i/100);if(i=i1+i2+i3)printf(%8d,i);printf(n);第 7章 函 数 习习 题题 七七 1简述函数的结构、函数的定义方法与函数的调用方法。2简述变量的作用域和变量的
36、存储类别。3简述递归函数和递归调用。4仔细阅读下列程序,给出程序的运行结果。(1)main()fun1();printf();fun2();void fun1()printf(COMPAQ);void fun2()printf(COMPUTER);第 7章 函 数(2)main()int i;for(i=1;i0)+x;+y;printf(x=%d,y=%d,x,y);main()int m=1;f(m);第 7章 函 数 5定义一个函数,求xn。6定义一个函数,求两个数的平均值。7定义一个函数,判断三个数能否构成三角形。8定义一个函数,输出九九乘法表。9编写程序,求100个实数的和及平均值,
37、要求用函数完成。10编写程序,求n个数的最大值和最小值,要求用函数完成。11编写程序,求两个数的最大公约数及最小公倍数,要求用函数完成。12编写程序,分离出一个实数的整数部分与小数部分,要求用函数完成。第 7章 函 数 13编写程序,读一组正整数,输出每个数除1和本身外的所有因子,如无因子则输出这个数是素数,要求用函数完成。14编写程序,计算1!+2!+31+n!的值,要求用函数完成。15编写程序,计算C+C+C的值,要求用函数完成。16编写四则运算(+、*、/)的训练程序,要求用函数完成。17编写程序,计算任意两个整数之间素数、完数的个数,要求用函数完成。18编写成绩处理程序,求100个同学4门课程的总成绩与平均成绩,要求用函数完成。第 7章 函 数 19编写程序,用递归方法求两个数的最大公约数。20编写程序,用递归方法求Fibonacci数列的前n项。21用递归方法解决第6章习题18的猴子吃桃子问题。22用递归方法将某十进制整数转换成二进制数。第 7章 函 数 23编写程序,进行下列数制转换,要求采用模块化程序设计。(1)十进制转二进制 (2)十进制转八进制 (3)十进制转十六进制(4)二进制转十进制 (5)八进制转十进制 (6)十六进制转十进制进一步请考虑对实型数据进行转换。24自命一题,给出带实际背景的问题,给出解决此问题的程序。要求包含有函数的嵌套调用。
侵权处理QQ:3464097650--上传资料QQ:3464097650
【声明】本站为“文档C2C交易模式”,即用户上传的文档直接卖给(下载)用户,本站只是网络空间服务平台,本站所有原创文档下载所得归上传人所有,如您发现上传作品侵犯了您的版权,请立刻联系我们并提供证据,我们将在3个工作日内予以改正。