1、深切哀悼四川汶川大地震遇难同胞,团结一心、众志成城! 做我们应该做的事情、做我们能够做的事情 !,第六章 函 数,教学要求 1理解函数的功能。 2掌握函数定义的一般形式(重点)。 3掌握函数的形参与实参的对应关系、参数传递方法及函数返回值的概念(难点) 。 4掌握函数调用的基本方法(重点) 。 5掌握函数嵌套调用的一般过程(重点) 。 6了解函数递归调用的几种形式。 7掌握数组名作为函数的参数的一些特点(难点) 。 8掌握局部变量与全局变量的概念及它们的使用特点(难点) 。 9掌握动态存储变量与静态存储变量的含义,会正确识别和使用。 10了解内部函数与外部函数的含义。,一、函数的定义(重点)
2、二、函数的调用(重点) 1、函数的一般调用 2、函数的嵌套调用 3、函数的递归调用 三、数组作为函数的参数(难点) 四、局部变量与全局变量(难点) 五、变量的存储类别(难点) 六、内部函数与外部函数,本章教学要点,函数的定义,1、概述 2、函数定义的方法(重点) 3、函数的参数(难点) 4、函数的返回值(难点),概述 1、C程序的基本结构(回顾) 2、什么是函数?函数是完成一个独立功能的程序代码。 3、例: 4、要点:,例exp6_1:无参函数的结构分析Include“stdio.h” main() printstar();print_message();printstar();printst
3、ar() printf(“*”);print_message() printf(“ how do you do!n”);,例exp6_2: 有参函数的结构分析#include main() int a,b,sum;a=10;b=24;sum=add(a,b);printf(”sum= %dn“,sum); int add(int x,int y) int z;z=x+y;return(z); ,对函数的认识: 一个C语言源程序可以由一个或多个函数组成。 C程序的执行总是从main( )开始,无论调用过多少其他函数,程序也是终止于main()函数。 C程序的所有的函数之间都是平行关系,不存在函数
4、的嵌套定义。 函数的分类: 从用户的角度对函数的分类: 标准库函数:由系统提供。 用户自定义函数:解决用户的专门需要。 从函数的参数角度对函数的分类: 无参数函数:函数定义与调用时不涉及参数,只用于执行指定的一组操作。 有参数函数:主调函数可以将数据传给被调函数使用,被调函数中的数据也可以带回给主调函数使用。,思考: 函数怎么定义? 参数如何定义?,函数定义的方法,1.无参数函数的定义 函数类型说明符 函数名() 如:printstar() 函数说明部分; printf(“*n”);语句块; 2.有参数函数的定义 函数类型说明符 函数名(形式参数表) 形参说明 如:int add(x,y) 函
5、数说明部分; int x,y;语句块; return x+y; 3.例: 4.要点:,例exp6_3:定义函数的方法分析 main() int a,b,c;scanf(“%d,%d”, ,要点: 1、函数的类型:即函数返回值的类型(缺省时为整型)。若不返回任何值,则应将其类型定义为void。 2、函数的命名: 规则:按“见名知意”或“匈牙利”表示法。 含义:函数名代表此函数在内存中的起始位置。 3、形参的定义: 形参的个数和类型均由函数的功能来决定。 4、空函数的定义: 函数体无任何语句。,函数的参数,1、形式参数写在函数定义中函数名后面括号中的变量叫做“形式参数”。 2、实际参数出现在函数调
6、用过程中函数名后面括号中的表达式称为“实际参数”.例: 3、要点: 形参变量只有在函数被调用时才占用内存空间,调用结束后所占空间即被释放; 在函数定义时必须指出形参的类型; 实参与形参的类型必须一致; 实参可以是常量,变量或表达式; 实参对形参的传递数据是单向传递(值传递); 可以将形参的类型说明放在参数表中.如:int max(int x ,int y).,1、函数的返回值是通过函数中的return语句来完成的,return语句一次只能返回一个值; 2、由return语句返回的数据类型应与函数定义时的类型一致。例: 3、当在函数的定义时,未指出函数的类型,c语言默认的类型为整型. 4、函数没
7、有return语句并不表明函数没有返回值,只是这个返回值对于调用者来说可能是没有用的,这个返回值随函数的不同它的值也不尽相同. 5、如果我们明确表示函数不能返回值,在函数的定义时用“void”加以说明,这样可以确保函数不带回返回值. 6、例:,函数的返回值,例exp6_4:对函数返回值的理解与分析 main() int a,b,c;a=printstar();b=print_message();c=printstar();printf(“%d,%d,%dn”,a,b,c); 若不需要函数带回任何值,可使用: void printstar() void print_message(),例exp6
8、_5:函数返回值的类型与其定义的类型不符的情况。 main() float a,b;int c;scanf(“%f,%f”,输入:1.5,2.5,输出:max is 2,函数的一般调用,1、函数调用的一般形式: 函数名(实参表列); 如:add(int x,int y) 的调用为add(2,3); 2、函数调用的规则: 基本原则:实参与形参在个数与类型上必须保持一致。 当函数无参数时,实参表列就为空,但括号不能少。 实参表列中的各个参数之间用逗号隔开。 实参与形参是一一对应的关系(函数调用的过程)。 对于实参表的求值顺序各个系统是不一致的。如: 3、函数调用的方式 4、函数的引用性说明,1.函
9、数语句: 功能:将函数调用作为一个语句。 如:printf(“book”); 要点:不要求函数带回值,只要求完成一定的操作。 2.函数表达式: 功能:将函数作为表达式的一部分。 如: c=2*max(a,b); 要点:要求函数带回一个确定的值,以参加表达式的运算。 3.一个函数作为另一个函数的参数: 功能:将函数的调用作为一个函数的实参。 如: m=max(a,max(b,c)); 要点:其实质也是函数表达式的调用。,函数调用的方式,1.被调用函数必须是一个已经存在的函数。 2.如果使用库函数应在文件头加上#include命令,以便将有关的库函数包含到本源文件中来。 3.对于用户自定义函数,函
10、数的定义部分应出现在该函数被调用之前。否则,在调用函数之前应作提前说明。 4.引用性说明的方法: 返回值类型 函数名( ); 如: int add( ); 5.举例:,函数的引用性说明(申明),例exp6_6:函数的引用性说明 main() float add( ); float add(x,y)float a,b,c; float x,y; int c; float z; scanf(“%f,%f”, ,输入:3.6,5.5,输出:sum is 9.10000,例exp6_7:分析程序(实参表求值顺序的一致),main() int f(int a,int b) int i=2,p; int
11、c;p=f(i,+i); if(ab) c=1;printf(“%d”,p); else if(a=b) c=0; else c=-1;return c; 改进的做法: j=i; k=+i; 或 j=+i; p=f(j,k) p=f(j,j),1、含义:所谓函数的嵌套调用是指一个函数在被调用时其本身又调用了其他函数. 2、嵌套调用的过程: main() a函数 b函数调用a函数 调用b函数结束 3、举例:,函数的嵌套调用,例exp6_8:函数的嵌套调用 swap(int a,int b) int t;t=a,a=b,b=t;printf(“%d,%d”,a,b);/嵌套调用 main() in
12、t a=3,b=5;swap(a,b);/调用printf(“%d,%d”,a,b);,1、含义:在调用一个函数的过程中又直接或间接的调用函数自身叫做函数的递归调用. 2、直接递归调用:例如 3、间接递归调用:例如 4、要点: 不论是直接调用还是间接调用,要使调用能够终止必须有一个使调用终止的条件,不然的话调用将陷入无终止状态. 5、应用举例,函数的递归调用,例exp6_9:函数的直接递归调用 int f(int x) int y,z;z=f(y);return (2*z);,例exp6_10:函数的间接递归调用int f1(int x) int y,z; z=f2(y); return (2
13、*z); ,int f2(t) int t; int a,c; c=f1(a); return (3+c); ,例exp6_11: 有五个人坐在一起,问第五个人有多少岁?他说比第四个人大两岁.问第四个人的岁数,他说比第三个人大两岁,问第三个人的岁数,他说比第二个人大两岁,.当问到第一个人时,他说自己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) in
14、t c;if(n=1)c=10;else c=age(n-1)+2;printf(“%dage 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,思考:“汉诺塔”问题!,例exp6_12: 用递归的方法求n!float fac(int n) float f;if (n0) printf(“n0,data error!”);else if (n=0|n=1) f=1;else f=fac(n-1)*n;return f; main() int n
15、;scanf(“%d”,n!=,1 (n=0,1),n*(n-1)! (n1),例exp6_13:求下面程序的输出结果。 fun(int k) if(k=0) return 3;return (fun(k-1)*3); main() int w=10;printf(“%dn”,fun(5)*w); ,运行结果为:7290,数组作为函数的参数,数组元素作为函数的实参 数组名作为函数的参数(难点) 二维数组作为函数的参数,1、含义:数组元素作为表达式的组成部分,与变量作实参一样,都是按“值传递”进行单向传送. 2、例exp6_14:程序分析 fun(int x) main() x+=3; int
16、a10=1,2,3,4,5,6,7,8,9,10,i; for(i=0;i10;i+) fun(ai);printf(“%5d”,ai); ,数组元素作为函数的实参,运行结果:1 2 3 4 5 6 7 8 9 10,1.数组名作为函数的参数,应在调用函数和被调用函数中分别定义数组. 2.形参与实参的类型应一致. 3.实参与形参的数组的大小可以一致也可以不一致,形参接收的是实参传来的数组的首地址,对数组的大小不作检查. 4.数组名作函数的参数传递时,不再是值传递,而是地址传递,实参传递给形参的不是一个简单的数值,而是一段内存单元的首地址,在被调用函数中对这段地址所指向的单元的内容改变时,将反映
17、到调用函数中, 5.例:,数组名作为函数的参数,例exp6_15:程序分析 #include fun(char s) int x=0,y;char c;for(y=strlen(s)-1;xy;x+,y-)c=sx;sx=sy;sy=c; main() char a=“abcdefgh”;fun(a);puts(a); ,运行结果:hgfedcba 数组名可作为函数的参数,这时形参和实参都是数组名(或指针)。,例如exp6_16:用选择法对数组中的10个数进行排序. 1、选择法的解题思路: 将10个数中的所有数逐个进行比较找出其中的最小数,将这个最小数与10个数中的a0进行交换; 将剩下的9个
18、数中的所有数逐个进行比较找出其中的最小数,将这个最小数与9个数中的a1进行交换; 以此类推. 2、程序设计:采用数组名作为函数的参数来实现一批数据的传递。,void sort(array,n) int array,n; int i,j,k,t;for(i=0;in-1;i+) k=i;for(j=i+1;jn;j+) if (arrayjarrayk) k=j;t=arrayk,arrayk=arrayi,arrayi=t; main() int a10; printf(“entre the arrayn”);for(i=0;i10;i+) scanf(“%d”,二维数组作函数的参数 1、用法
19、:多维数组名也可以作函数的参数,从调用函数到被调用函数依然传递的是地址,形参中的数组定义可以省略第一维的大小说明,但第二维的说明不可少. 2、例exp6_17: 有一个3*4的矩阵,求出其中的最大元素的值. max_value(int array4) int i,j,k,max=array00;for(i=0;imax) max=arrayij;return max; main() static int a34=1,3,5,7,2,4,6,8,15,17,34,12;printf(“max value is %dn”,max_value(a);,局部变量与全局变量,局部变量的含义、应用与使用要
20、点 全局变量的含义、应用与使用要点,局部变量 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有效,全局变量1、含义:在所有的函数之外定义的变量称为外部变量(或叫做全局变量).其作用范围是从变量定义的
21、位置开始,直到本源文件的结束。如: 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(int x,int y)/*定义函数f2*/ int i,j; main() int m,n; ,全局 变量p,q的有 效范围,全局 变量c1,c2的有 效范围,(1) 一个源文件可以包含一个或几个函数. (2)全局变量可以被本源文件的所有函数共享. (3)全局变量的设置增强了函数间数据的联系.一个函数对全局变量的值的改变将会影响到其他
22、的函数中.例:(用于返回多个函数值) (4)建议在只有必要时才设置全局变量. (5)全局变量在文件的开头定义,则它的有效范围是整个源文件,但如果全局变量不是在开头定义的,则它有效范围是从定义点到源文件的结束.如果想在全局变量的定义点之前引用时,需要用关键字“extern”作提前引用说明. (6)当全局变量名与局部变量名相同时,则在局部变量的有效范围内全局变量不起作用.,例exp6_18:有一个一维数组,内放10个学生成绩,编写函数求出平均分、最高分和最低分。float max=0,min=0; main() float average(array,n) float ave,score10; f
23、loat 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);,例exp6_19:分析下面的程序写出结果. plus(int x,int y) int z;z=x+y+;return z;mian() extern int a,b;int z; z=plus(a,b);printf(“%dn”,z);int a=13,b=-8,z=10; 运行结果;5,例exp6_20:分
24、析下面的程序写出结果f1(int x,int y) f2(int x,int y) int z; int z; return z=xy?x:y; 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;,变量的存储类别,从变量存在时间(生存期)角度来区分: 1、静态存储变量: 在程序运行期间分配固定的存储空间的方式。 2、动态存储变量: 在程序运行期间根据需要进行动态的分配存储空间的方式。 3、要点: 1)用户
25、使用的内存空间:(见图) 2)变量和函数的存储类别: 3)不同存储类别变量的作用域与生存期的特点,内存中用户区的划分,1.程序区:存放程序的代码; 2.静态存储区:存放全局变量和静态的局部变量; 3.动态存储区:存放 函数的局部变量, 函数的形参变量, 函数调用时的现场保护和返回地址;,存放在静态存储区中的变量的生存周期是程序的整个执行过程。 存放在动态存储区中的变量的生存周期是函数的执行期间。,变量和函数的存储类别 1、存储类别的含义:指数据在内存中的存储方法。 2、对存储方法的理解:在C语言中,对变量和函数的定义分为两个层次: 数据类型的定义 数据存储类别的定义如:static int a
26、,b; 3、存储类别: 自动变量(auto) 静态变量(static) 寄存器变量(register) 外部变量(extern) 分别放在静态存储区和动态存储区。,存储类别,数据类型,1、含义:函数中定义的变量不作特殊说明都为自动局部变量,存储在动态存储区。 2、用法:用关键字auto进行说明。如: int f(a) int a; auto int b=0,c=10; 3、要点: 一般情况下,可以省略auto关键字,当函数调用结束后,它们所占用的存储空间即被释放.,自动变量,1、含义:函数中的局部变量的值在函数调用结束后不消失而保留原值。 (即所占用的存储单元并不释放,下次直接使用) 2、用法
27、:用static说明。例如: 3、要点:,局部静态变量,例exp6_21:分析下面程序的运行过程与结果 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,说明: 1.局部静态变量存储在静态存储区,在程序的整个运行期间都不释放。 2.局部静态变量是在编译时赋初值的,即只赋值一次。 3.局部静态变量在定义时不赋初值,编译时系统自动赋初值0;而对于自动变量,如果不赋初值则它的值是一个不确定的值。 4
28、.c语言规定只有全局变量数组和局部静态变量数组才能进行初始化。 5.局部静态变量在函数调用结束后仍然存在,但只能被这个函数再次调用,不能被其他函数调用。,寄存器变量 1、含义:寄存器是计算机CPU的重要组成部分, 在c语言中允许将一些频繁使用的变量存放在计算机的寄存器中,以节省运算时间,提高效率. 2、用法:用register说明。如: 3、要点: 只有局部自动变量和形参可以作为寄存器变量,其它变量不允许。 一个计算机系统中的寄存器数目是有限的,不能定义任意多个寄存器变量。 不同系统对register变量的处理方法不同。 如:MSC和TC对register只当作自动变量处理。PDP11则只将i
29、nt、char和指针变量定义成register变量 局部静态变量不能定义为register变量。,例exp6_22:分析下面程序 int fac(int n) register 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、基本含义: 全局变量是在函数的外部定义的,存放在内存的静态存储区中,它可以被程序中的所有函数所引用。 2、注意问题: 允许其他文件中的函数引用.如果在一个文件中引用另一个文件中定义的全局变量,应当在需要引
30、用的地方,使用extern进行说明。例: 当在程序中希望全局变量只限于在本源文件使用不能被其他的文件中的函数引用,可以在定义这个全局变量时用static进行说明,这时的static不代表静态的含义。,例exp6_23:给定b的值,输入a的值,求a*b和am的值。 file1.c int a; main() int power();int b=3,c,d,m;printf(“enter the number a and its power:n”);scanf(“%d,%d”, ,file2.c extern int a; power(int n) int i,y=1; for(i=1;i=n;i
31、+) y*=a;return(y); ,变量生存时间,变量定义,变量作用域,内部函数与外部函数,一、内部函数 格式:在函数定义时加上static.即: static 类型标识符 函数名(形参表) 要点:内部函数又称为静态函数这样的函数只限在所在的文件中。 二、外部函数 格式:在函数定义时加上extern即: extern 类型标识符 函数名(形参表) 要点:函数被冠以extern说明函数为外部函数,可以被其他文件中的函数所调用,当一个函数在定义时,未说明static时隐含的类型为extern。 三、例:,例exp6_23:有一个字符串,内有若干个字符,今输入一个字符,程序将字符串中该字符删去,
32、用外部函数实现。 file1.c main() extern enter_string(),delete_string(),print_string();char c;static char str80;enter_string(str);scanf(“%c”, ,运行情况: 输入:abcdefgc(回车)c(回车) 输出:abdefg,file2.c file4.c #include “stdio.h” extern print_string(str) extern enter_string(str) char str ; char str80; printf(“%s”,str); gets(str);file3.c extern delete_string(str,ch) char str ,ch; int i,j; for(i=j=0;stri!=0;i+)if(stri!=ch) strj+=stri;strj=0;,