1、,第五章 函数,本章目标,1. 进一步理解多个函数构成一个C程序,2. 进一步了解和熟悉库函数,3. 学会编写自已的函数,4. 理解函数的调用关系,5. 理解函数中参数的传递机制,一、C程序模块,一个C程序可由一个main( )函数和多个其它函数构成。,5.1 C程序模块, 其它函数指的是:, 系统函数由系统提供,放在头文件中,自编函数由程序设计人员编写,二、函数的调用,如:, 所有的函数都 具有两种特征:, 返回值,参数,(返回值和参数都是可缺省的),5.2 库函数,一、库函数简介,系统自带的标标准库函数根据不同的功能作用放在不同的头文件中。,如:stdio.h用于标准输入/输出,math.
2、h用于数学计算,ctype.h用于字符处理,string.h用于字串处理,time.h用于时间/日期的处理,dir.h用于控制目录和路径,graphics.h用于图形操作,dos.h,bios.h,用于接口处理,二、库函数的作用, 使程序更具有“ 独立性”和“ 可移植”性。, 使用时只需在程序的开头加上一条语句:,#include “ ”,根据使用的函数来确定, 使程序具有“ 模块化”。,例:求两个浮点型数值x/y的余数。,(注意:不能写成3.6%1.7 =2.0),#include #include main( ) float x, y, z;scanf(“%f%f“, ,例:函数的结构 m
3、ain() printstar();print_message();printstar();printstar() printf(“*”);print_message() printf(“ how do you do!n”);,对函数的理解: 一个源程序由一个或多个函数组成。 一个c程序由一个或多个源程序组成。 c程序的执行顺序从main()开始,调用其他函数后,流程回到main()函数。 c程序的所有的函数都是平行的,不存在函数的嵌套定义。 从用户的角度对函数的分类: (1)标准库函数:由系统提供。 (2)用户自定义函数:解决用户的专门需要。 从函数的形参对函数的分类: (1)有参数函数:用
4、于执行指定的一组操作,可以带回或不带回函数值。 (2)无参数函数:主调函数可以将数据传给被调函数使用,被调函数中的数据也可以带回给主调函数使用。,5.3 自定义函数,一、自定义函数的作用, 使程序具有“ 积木”功能。(模块化), 使程序具有“ 重构”功能。,1、含义:函数是完成一个独立功能的程序代码。 2、C程序的基本结构(如图):,1、函数的返回值是通过函数中的return语句来完成的,return语句一次只能返回一个值; 2、由return语句返回的数据类型应与函数定义时的类型一致。例: 3、当在函数的定义时,未指出函数的类型,c语言默认的类型为整型. 4、函数没有return语句并不表明
5、函数没有返回值,只是这个返回值对于调用者来说可能是没有用的,这个返回值随函数的不同它的值也不尽相同. 5、如果我们明确表示函数不能返回值,在函数的定义时用“void”加以说明,这样可以确保函数不带回返回值.return z 等价 return(z)return (xy?x:y); 6、例:,函数的返回值,例:对函数返回值的理解: main() int a,b,c;a=printstar();b=print_message();c=printstar();printf(“%d,%d,%dn”,a,b,c); 若不需要函数带回任何值,可使用: void printstar() void print
6、_message(),例:函数返回值的类型与其定义的类型不符时: main() float a,b;int c;scanf(“%f,%f”, /* return(xy?x:y) 等价 */,函数的一般调用,1、函数调用的一般形式: 函数名(实参表列); 如:add(int x,int y) 的调用为add(2,3) 2、函数调用的规则: 实参与形参在个数与类型上必须保持一致。 当函数无参数时,实参表列就为空,但括号不能少。 实参表列中的各个参数之间用逗号隔开。 实参与形参是一一对应的关系(函数调用的过程)。 对于实参表的求值顺序各个系统是不一致的。,1.函数语句: 功能:将函数调用作为一个语句
7、。 如:printf(“book”); 要点:不要求函数带回值,只要求完成一定的操作。 2.函数表达式: 功能:将函数作为表达式的一部分。 如: c=2*max(a,b); 要点:要求函数带回一个确定的值,以参加表达式的运算。 3.一个函数作为另一个函数的参数: 功能:将函数的调用作为一个函数的实参。 如: m=max(a,max(b,c)); 要点:其实质也是函数表达式的调用。,函数调用的方式,1.被调用函数必须是一个已经存在的函数. 2.如果使用库函数应在文件头加上#include命令,以便将有关的库函数包含到本源文件中来. 3.对于用户自定义函数,函数的定义部分应出现在该函数被调用之前.
8、不然的话,在调用函数之前应作提前说明. 4.引用性说明的方法: 一般形式: 函数类型 函数名(形参类型 形参名, );或 函数类型 函数名(); 作用:告诉编译系统函数类型、参数个数及类型,以便检验 返回值类型 函数名( ) 如: int add( );,函数的引用性说明,例1: 求二个数中的最大值int max (x, y)int x, y;int z; z=xy? x:y;return (z);,用return语句,返回函数的值。,或:int max (int x, int y)int z; z=xy? x:y;return (z);,四、举例,通过函数调用求二个数中的最大值:,#incl
9、ude ,int max (int, int); /*函数原型*/,main( ), int a, b, t;,scanf(“%d%d“, ,t=max(a, b); /*函数调用*/,printf(“value of maximize is: t=%d“,t);,int max(int x, int y) /*函数定义*/, int z;,z=xy? x:y;,return(z);,#include float add( ); /*函数说明*/ main ( ) float a, b, c; scanf(“%f, %f“,float add( float x, float y); /*函数定
10、义*/ float z;z=x+y;return z; ,求二实数之和,规则:若函数的定义放在main( )函数之前可省略函数说明。,第五章 函数,#include float add( float x, float y); /*函数定义*/ float z; z=x+y; return z; main ( ) float a, b, c; scanf(“ %f, %f”,求二实数之和(将例1程序改写如下:),第五章 函数,1. 形参与实参,函数被调用时,临时分配单元给形参,调用完毕, 这些单元被释放。,实参: 出现在调用函数中, 形参: 出现被调用函数中。,五、自定义函数中的几个重要特征,
11、实参可为表达式,只传递表达式的值。, 实参、形参类型一致。, 可在形参表列中对形参说明。,注:,! ?,2. 函数返回,或 return 变量的值;,通过 return 语句将流程返回主调函数。, return; 函数无返回值, return 表达式的值;,3. 函数名,要选择一个简洁的有意义的名字作为函数名。,从函数名可以反映出函数的功能。,六、传值调用和传引用(传址)调用,1. 传值调用,方式:函数的实参和形参的类型均为简单变量。,系统会建立一份实参的拷贝给形参。,当函数调用完毕,这份实参的拷贝消失。,特点:传值调用不会影响实参的值。,形参与实参 形式参数:定义函数时函数名后面括号中的变量
12、名 实际参数:调用函数时函数名后面括号中的表达式,例 比较两个数并输出大者,main() int a,b,c;scanf(“%d,%d“, ,2. 传引用(传址)调用,形参变量和实参变量共同内存地址。 地址传递:数组名和指针(共用内存单元),特点:形参变量的值的变化会影响实参的值的改变。,5.4 函数的存储类型,一、标识符的属性,一个标识符的属性除了前面已讲过的基本属性外,还具有一些其它的属性:, 存储类别, 存储期,函数中的变量,1、按作用域分局部变量:在本函数内部有效全局变量:从定义位置到源程序结束均有效2、按存储类型分静态存储变量:程序运行时有固定的存储空间, 结束时释放内存单元动态存储
13、变量:根据需要动态分配和释放内存单元,举例说明,二、存储类别,1. 存储类型和存储期, 自动型 (auto), 寄存器型(register), 外部型(extern), 静态型(static),自动存储期,静态存储期,2. 存储期的性质, 自动存储:只有变量才具有自动存储期,如函数中的变量说明:,auto float x, y;,auto int m, n;,作用:进入到函数块中,该变量存在,退出函数块后该变量消失。程序中大部分变量都具有自动存储期。, 静态存储:可用于变量和函数,如:static int;,作用:从程序开始执行起就一直存在,始终占据内存单元,直到程序结束运行。, 对静态存储变
14、量,若定义时赋初值,则程序运行中仅在第一次调用时赋初值,下一次调用不再赋初值,而是使用上一次调用的值。,特别提醒:, 对静态存储变量,若定义时不赋初值,则系统置初值0,而动态变量不赋初值则值不确定。,#include int fac (n) /*函数定义*/ int n; static int f=1; f=fn; return(f); main ( ) int i; for (i=1; i=5; i+) printf (“%d!=%dn“, i, fac(i); ,例:求n!,第五章 函数,1!12!23!64!245!120,每一次调用fac(i),打印一个i!,同时保留这个i!的值以便下
15、次再乘(i+1)。,运行结果为:,5.5 标识符的作用域, 标识符可以具有四种作用域。, 函数作用域, 文件作用域, 块作用域, 函数原型作用域,(作用域: 有效范围), 可根据标识符的作用域区分局部变量和全局变量。,一、局部变量,凡在函数(含main 函数)内部定义的变量称为局部变量。,局部性: 局部变量仅在函数内部有效。,2. 形参为局部变量。,3. 在复合语句中可定义仅在复合语句中有效的临时变量。,1. 不同的函数可有同名同类型的变量,它们占不同的内存单元, 互不影响。,二、全局变量,一个源文件中, 在所有函数之外定义的变量为全局变量。,有效性: 自定义位置开始至文件结尾全部有效。,例:
16、 int p=1, q=5;float f1(a)int a;int b, c; char c1, c2;,p,q的作用范围,c1, c2的作用范围,char f2(x,y); int x, y; int i, j; main ( ) ,第五章 函数,1. 全局变量所作用到的函数,相当于这些函数的公共变量。当一个函数对其值进行改变后,另一个函数使用该变量的值亦相应改变。好处: 函数之间值传递。,2. 不要随意使用全局变量。一是始终占据内存单元;二是由于函数依赖于外部定义的变量,减少了通用性。,注意,3. 不在作用域内函数。若使用全局(外部)变量,需在函数体内加上extern保留字。,4. 全局
17、和局部变量同名时,局部变量有效。,局部变量1、含义:在一个函数内部定义的变量,其作用范围为函数体内部。 2、应用 3、使用要点: (1)主函数中定义的变量也只在主函数中有效; (2)不同的函数中可以使用相同的函数名,它们代表不同的含义; (3)形参也是局部变量; (4)在一个函数内部可以使用复合语句来定义变量,这样的变量只在本复合语句中有效.,int max(int a,int b) int t; fun() int i; main() int m,n; ,m,n有效,a,b,t有效,i有效,局部变量-内部变量 定义:在函数内定义,只在本函数内有效 说明: main中定义的变量只在main中有
18、效 不同函数中同名变量,占不同内存单元 形参属于局部变量 可定义在复合语句中有效的变量 局部变量可用存储类型:auto register static (默认为auto),局部变量与全局变量,运行结果:max=8,例 外部变量与局部变量,全局变量1、含义:在所有的函数之外定义的变量称为外部变量(或叫做全局变量).其作用范围是从变量定义的位置开始,直到本源文件的结束。如: 2、要点: 3、举例:,int p=1,q=1;/*定义全局变量p,q*/ float f1(a) /*定义函数f1*/ int a; int b,c; char c1,c2; /*定义外部变量c1,c2*/ char f2(
19、int x,int y)/*定义函数f2*/ int i,j; main() int m,n; ,全局 变量p,q的有 效范围,全局 变量c1,c2的有 效范围,(1) 一个源文件可以包含一个或几个函数. (2)全局变量可以被本源文件的所有函数共享. (3)全局变量的设置增强了函数间数据的联系.一个函数对全局变量的值的改变将会影响到其他的函数中.例:(用于返回多个函数值) (4)建议在只有必要时才设置全局变量. (5)全局变量在文件的开头定义,则它的有效范围是整个源文件,但如果全局变量不是在开头定义的,则它有效范围是从定义点到源文件的结束.如果想在全局变量的定义点之前引用时,需要用关键字“ex
20、tern”作提前引用说明. (6)当全局变量名与局部变量名相同时,则在局部变量的有效范围内全局变量不起作用.,例:写一个函数,找出100-200之间的全部素数。 #include “stdio.h” #include “math.h” int isprime(int d); int i,s;s=sqrt(d);for(i=2;i=s;i+)if(d%i=0)return 0;return 1; main() int m,n=0;for(m=100;m=200;m+) if(isprime(m) if(n%10=0)printf(“n”); n+; printf(“%d”,m); ,例:有一个一
21、维数组,内放10个学生成绩,写一个函数,求出平均分、最高分和最低分。略float max=0,min=0; main() float average(array,n) float ave,score10; float array ;int n; int i; int i; for(i=0;imax) max=arrayi; max,min,ave);else if (arrayimin) min=arrayi;sum=sum+arrayi;aver=sum/n; return(aver);,例:分析下面的程序写出结果. plus(int x,int y) int z;z=x+y+;return
22、 z;mian() extern int a,b;int z; z=plus(a,b);printf(“%dn”,z);int a=13,b=-8,z=10; 运行结果;5,例:分析下面的程序写出结果 f1(int x,int y) int z; return z=xy?x:y; return z; f2(int x,int y) int z; z=xy?+x:+y; return z; main() extern int a,b;printf(“%d,%d,%dn”,a,b,f1(a,b);printf(“%d,%d,%dn”,a,b,f2(a,b); int a=20,b=-15; 20,
23、-15,20 / 20,-15,21,例 输入长方体的长(l)、宽(w)、高(h),求长方体体积及正、侧、顶三个面的面积。 /*功能:利用全局变量计算长方体的体积及三个面的面积*/ int s1,s2,s3; int vs(int a,int b,int c) int v;v=a*b*c; s1=a*b; s2=b*c; s3=a*c;return v;main() int v,l,w,h;clrscr();printf(“n input length,width and height: “);scanf(“%d%d%d“, 程序演示,#include int i=1; main() stat
24、ic int a;register int b=-10;int c=0;printf(“-MAIN-n“);printf(“i:%d a:%d b:%d c:%dn“,i,a,b,c);c=c+8;other();printf(“-MAIN-n“);printf(“i:%d a:%d b:%d c:%dn“,i,a,b,c);i=i+10;other(); ,other() static int a=2;static int b;int c=10;a=a+2; i=i+32; c=c+5;printf(“-OTHER-n“);printf(“i:%d a:%d b:%d c:%dn“,i,a,
25、b,c);b=a; ,-Main- i:1 a:0 b:-10 c:0,-Other- i:33 a:4 b:0 c:15,-Main- i:33 a:0 b:-10 c:8,-Other- i:75 a:6 b:4 c:15,8,4,33,15,4,43,6,75,15,6,例 变量的寿命与可见性,例: f(a) int a; auto int b=0;static int c=3;b+=1;c+=1;return (a+b+c); main() int a=2,i;for(i=0;i3;i+)printf(“%3d”,f(a); 运行结果:7 8 9,例: int fac(int n) r
26、egister int i,f=1;for(i=1;i=n;i+) f=f*i;return f; main() int i;for(i=1;i=5;i+)printf(“%d!=%dn”,i,fac(i); ,结果: 1!=1 2!=2 3!=6 4!=24 5!=120,1、含义:所谓函数的嵌套调用是指一个函数在被调用时其本身又调用了其他函数. 2、嵌套调用的过程: main() a函数 b函数调用a函数 调用b函数结束 3、举例:,函数的嵌套调用,例7:函数的嵌套调用 swap(int a,int b) int t;t=a,a=b,b=t;printf(“%d,%d”,a,b);/嵌套调
27、用 main() int a=3,b=5;swap(a,b);/调用printf(“%d,%d”,a,b);,#include int dif(int x,int y,int z);int max(int x,int y,int z);int min(int x,int y,int z); void main() int a,b,c,d;scanf(“%d%d%d“,int dif(int x,int y,int z) return max(x,y,z)-min(x,y,z); int max(int x,int y,int z) int r;r=xy?x:y;return(rz?r:z);
28、int min(int x,int y,int z) int r;r=xy?x:y;return(rz?r:z);,例 求三个数中最大数和最小数的差值,5.6 递归调用,一、递归的特点,一个问题能够成为递归必须具备的条件是:, 后一部分与原始问题类似, 后一部分是原始问题的简化,1. 直接递归调用:函数直接调用本身,二、程序中的递归方式,2. 间接递归调用:函数间接调用本身,以下表示了递归的概念.,显然: 上述例子会无限递归 (无限执行)。所以, 在递归调用时都必须有条件限制。,当条件成立, 调用递归, 否则结束。,例: 求n!,1. 从数学上定义,#include long fac(int
29、n) /*函数定义, 计算n!*/ long f; if (n0) printf(“input error!n“); else if (n= = 0 n= =1) f =1; else f =nfac(n 1); return (f); ,2. 程序:,第五章 函数,main ( ) int n;long y;printf(“input a integer! “)scanf (“%d“, ,第五章 函数,3. 执行过程:,设: 输入 5 (n=5),第1次调用:y=fac(5) 返回:y=5fac(4),第2次调用:y=5*4fac(3),第3次调用:y=5*4*3fac(2),第4次调用:y
30、=5*4*3*2fac(1),第5次调用:y=5*4*3*2*1*fac(0),例: 有五个人坐在一起,问第五个人有多少岁?他说比第四个人大两岁.问第四个人的岁数,他说比第三个人大两岁,问第三个人的岁数,他说比第二个人大两岁,.当问到第一个人时,他说自己10岁,请问第五个人的岁数? age(5)=age(4)+2 age(4)=age(3)+2 age(3)=age(3)+2 age(2)=age(1)+2 age(1)=1010 (n=1)age(n-1)+2 (n1),age(n)=,递归调用的过程,程序代码: age(int n) int c;if(n=1)c=10;else c=age
31、(n-1)+2;printf(“No%d age is %dn”,n,c);return c; main() age(5); 运行结果为: 1 age is 102 age is 123 age is 144 age is 165 age is 18,例:求下面程序的输出结果。 fun(int k) if(k=0) return 3;return (fun(k-1)*3); main() int w=10;printf(“%dn”,fun(5)*w); 运行结果为:7290,作业1,编程计算组合C nm 的函数C nm = _m!_n! (m-n)!,作业2,利用递归算法求:M!+M+MN,利
32、用递归算法求:M!+M+MN main() int m,n;scanf(“%d,%d“, ,利用递归算法求:M!+M+MN int f(m) int m; if(m=1)return 1;elsereturn m*f(m-1); ,利用递归算法求:M!+M+MN int g(m) int m; if(m=1)return 1;elsereturn m+g(m-1); ,利用递归算法求:M!+M+MNint k(m,n) int m,n; if(n=1)return m;elsereturn m*k(m,n-1); ,利用递归算法求:M!+M+MNint f(m) int m; if(m=1) return 1;else return m*f(m-1);int g(m) int m; if(m=1) return 1;else return m+g(m-1);int k(m,n) int m,n; if(n=1) return m;else return m*k(m,n-1); main() int m,n;scanf(“%d,%d“,