1、第八章第八章 函函 数数第1页第1页主要内容主要内容 函函函函 数数数数变量存储属性变量存储属性编译预处理编译预处理第2页第2页模块化程序设计基本思想模块化程序设计基本思想(自顶向下,逐步分解)现实问题现实问题分解为若干子问题集合分解为若干子问题集合对每个子问题单独编程(模块对每个子问题单独编程(模块函数)函数)比如:高校事务管理系统比如:高校事务管理系统第3页第3页分析工具:树型图树型图问题问题问题问题1问题问题2问题问题N问题问题21第4页第4页例例:用树型图分析高校事务管理系统用树型图分析高校事务管理系统高校事务管理高校事务管理办办公公室室管管理理教务管理教务管理学籍管理学籍管理科科研研
2、管管理理人人事事管管理理财财务务管管理理设设备备管管理理后后勤勤管管理理图图书书馆馆管管理理成绩管理成绩管理排课排课第5页第5页C是模块化程序设计语言C程序结构&C是函数式语言&必须有且只能有一个名为main主函数&C程序执行总是从main函数开始,在main中结束&C程序中主函数调用其它函数,其它函数间可互相调用&函数不能嵌套定义,能够嵌套调用第6页第6页算法实现算法实现main()f1()f5()f51()f2()f6()f4()f3()f7()f8()f52()f53()第7页第7页函数基本概念函数基本概念含有特定功效及界面程序段含有特定功效及界面程序段如:求和、排序、求最大(小)、特定
3、格式打印等。如:求和、排序、求最大(小)、特定格式打印等。函数作用函数作用便于实现模块化程序设计便于实现模块化程序设计长处:结构清楚修改以便通用性强长处:结构清楚修改以便通用性强函 数第8页第8页1 1、主函数、主函数 main()main()2 2、功效子函数、功效子函数函数函数1 1、库函数、库函数2 2、自定义函数、自定义函数使用使用库函数库函数应注意:应注意:1、函数功效、函数功效2、函数参数数目和顺序,及各参数意义和类型、函数参数数目和顺序,及各参数意义和类型3、函数返回值意义和类型、函数返回值意义和类型4、需要使用包括文献、需要使用包括文献使用使用使用使用自定义函数自定义函数时应注
4、意:时应注意:1、如何定义函数、如何定义函数-函数首部、函数首部、函数体函数体2、函数返回值意义和类型、函数返回值意义和类型3、如何调用自定义函数(注意事项)、如何调用自定义函数(注意事项)第9页第9页【函数定义】【函数定义】描述函数功效描述函数功效定义格式定义格式:(当代格式)(当代格式)类型类型 函数名称(类型函数名称(类型 参数参数1,类型,类型 参数参数2,)函数体函数体 函数返回值类型,缺省为函数返回值类型,缺省为int,void为空类型为空类型,表示函数表示函数不返回任何值不返回任何值可有可无(可有可无(void)例:例:ff().ff(void).等价等价例 有参函数(当代风格)
5、int max(int x,y)int z;z=xy?x:y;return(z);例 有参函数(当代风格)int max(int x,int y)int z;z=xy?x:y;return(z);例 无参函数 printstar()printf(“*n”);或 printstar(void)printf(“*n”);例 空函数 void dummy(void)函数体为空第10页第10页函数类型函数类型 函数名(形参表)函数名(形参表)形参类型阐明形参类型阐明阐明部分阐明部分语句部分语句部分老式风格:例例 有参函数(老式风格)有参函数(老式风格)int max(x,y)int x,y;int z
6、;z=xy?x:y;return(z);第11页第11页【函数调用【函数调用1】函数名(实参表列);函数名(实参表列);可缺省例:例:putchar(*);c=getchar();注:注:1、被调用、被调用函数存在函数存在(已被定义(已被定义(两重含义两重含义)2、调用库函数时应包括相应头文献、调用库函数时应包括相应头文献#include sqrt(40.0)#include strlen()#include 举例:编写子函数打印举例:编写子函数打印“*#”第12页第12页主、子函数之间关系主、子函数之间关系 从主函数开始执行,主调子,子不能从主函数开始执行,主调子,子不能调主,子与子之间能够
7、互相调用。调主,子与子之间能够互相调用。主调函数被调函数注:函数不能嵌套定义注:函数不能嵌套定义第13页第13页【函数申明】【函数申明】原则上是原则上是先定义后使用先定义后使用子子2 2主主主主子子1 1子子3 3处理办法:处理办法:2、在主调函数中(调用语句之前)、在主调函数中(调用语句之前)对被调函数进行申明。对被调函数进行申明。3、外部(显式)申明、外部(显式)申明类型类型 函数名称(类型函数名称(类型 参数参数1,类型,类型 参数参数2,);函数申明格式:函数申明格式:子子1 1子子2 2子子3 3主主主主1、被调用函数定义在前、被调用函数定义在前第14页第14页例例 函数申明举例函数
8、申明举例main()float a,b;int c;scanf(%f,%f,&a,&b);c=max(a,b);printf(Max is%dn,c);max(float x,float y)float z;z=xy?x:y;return(z);int型函数可不作函数阐明(Borland C+不行)/*ch7_5.c*/float add(float x,float y)float z;z=x+y;return(z);main()float a,b,c;scanf(%f,%f,&a,&b);c=add(a,b);printf(sum is%f,c);被调函数出现在主调函数之前,不必函数阐明ma
9、in()float add(float,float);/*function declaration*/float a,b,c;scanf(%f,%f,&a,&b);c=add(a,b);printf(sum is%f,c);float add(float x,float y)float z;z=x+y;return(z);float add();第15页第15页函数返回函数返回return(表示式表示式);return;或或 函数类型函数类型 在定义函数时,由函数类型要求。在定义函数时,由函数类型要求。应与应与return(表示式表示式)中表示式值中表示式值 类型一致类型一致无返回值时定义为空
10、类型无返回值时定义为空类型程序执行控制返回到主调函数中程序执行控制返回到主调函数中注:一个函数中可有多个return语句,但仅有一个起作用若函数类型与若函数类型与return语句中表示式值类型不一致,语句中表示式值类型不一致,按前者为准,自动转换按前者为准,自动转换-函数调用转换函数调用转换第16页第16页 printstar()printf(*);main()int a;a=printstar();printf(%d,a);例例 函数带回不拟定值函数带回不拟定值输出:10void printstar()printf(*);main()int a;a=printstar();printf(%d
11、,a);编译错误!第17页第17页例例 函数返回值类型转换函数返回值类型转换main()float a,b,c;scanf(%f,%f,&a,&b);c=max(a,b);printf(Max is%fn,c);max(float x,float y)float z;z=xy?x:y;return(z);设输入设输入 5.4,3.5输出:输出:5.000000第18页第18页【函数调用【函数调用2】有参函数调用过程有参函数调用过程有参函数调用过程:实参有参函数调用过程:实参=形参形参传递形式:传递形式:1、赋值调用、赋值调用 2、赋地址调用、赋地址调用main()int a,b;printf(
12、“%dn”,max(a,b);max(int a1,int b1)return(a1b1?a1:b1);形参从相应实参取得数据形参从相应实参取得数据第19页第19页main()int a,b,c,s;scanf(“%d,%d,%d”,&a,&b,&c);s=sum(a,b,c);printf(“sm=%dn”,s);sum(int x,int y,int z)int s;s=x+y+z;return(s);x,y,zx,y,z为暂时存储单元,调用开始分为暂时存储单元,调用开始分派空间、结束释放派空间、结束释放a?b?c?s?输入输入1,2,3123x?y?z?s?12366第20页第20页参数
13、传递形式包括:参数传递形式包括:1、值传送、值传送 (传值调用)(传值调用)2、地址传送、地址传送 (学过指针后再详讲(学过指针后再详讲)xyz123123abc1、形参改变不影响 实参(因为存放空间不同)2、在执行被调函数时形参单元才被建立,被调函数执行完后,存放单元被撤消v阐明:l实参必须有拟定值l实参表求值顺序,因系统而定(实参表求值顺序,因系统而定(Turbo C 自右向左自右向左)l实参与形参实参与形参个数相等,类型一致,按顺序一一相应个数相等,类型一致,按顺序一一相应l若形参与实参类型不一致,自动按形参类型转换函数调用转换l形参在函数被调用前不占内存;函数调用时为形参分派内存;调用
14、结束,内存释放,形参值不改变实参值第21页第21页main()int i=2,p;p=f(i,+i);printf(%d,p);int f(int a,int b)int c;if(ab)c=1;else if(a=b)c=0;else c=-1;return(c);例例 参数求值顺序参数求值顺序设参数求解顺序为从左到右运营结果:运营结果:-1设参数求解顺序为从右到左运营结果:运营结果:0main()int i=2,p;p=f(i,i+);printf(%d,p);int f(int a,int b)int c;if(ab)c=1;else if(a=b)c=0;else c=-1;retur
15、n(c);从右到左若程序运营结果:若程序运营结果:1分析编译系统参数执行顺序分析编译系统参数执行顺序为了程序可移植性,参数中不要含不拟定原因为了程序可移植性,参数中不要含不拟定原因返回第22页第22页711x:y:调用前:调用前:调用结束:调用结束:711x:y:例 互换两个数#include main()int x=7,y=11;printf(x=%d,ty=%dn,x,y);printf(swapped:n);swap(x,y);printf(x=%d,ty=%dn,x,y);swap(int a,int b)int temp;temp=a;a=b;b=temp;调用:调用:711a:b:
16、711x:y:swap:711x:y:117a:b:temp返回第23页第23页程序执行流程:程序执行流程:函数调用方式函数语句:例 printstar();printf(“Hello,World!n”);函数表示式:例 m=max(a,b)*2;函数参数:例 printf(“%d”,max(a,b);m=max(a,max(b,c);第24页第24页函数嵌套调用函数嵌套调用主函数调用子函数,主函数调用子函数,子函数调用子函数子函数调用子函数 C语言不允许函数嵌套定义,语言不允许函数嵌套定义,但允许函数嵌套调用。但允许函数嵌套调用。例题例题函数递归调用函数递归调用子函数直接或间接调用本身子函数
17、直接或间接调用本身f函数函数f函数函数f1函数函数调用调用f2函数函数f2函数函数调用调用f1函数函数*可用可用if语句来终止递归调用语句来终止递归调用*main()调用函数a结束a函数b函数调用函数b第25页第25页void star(int);void line();main()star(20);void star(int d)int i;for(i=1;i10)line();else printf(“*”);void line()printf(-);main 函数函数 star 函数函数 line 函数函数调用调用 结束结束调用调用函数嵌套调用举例函数嵌套调用举例返回第26页第26页函数
18、递归调用举例函数递归调用举例1*2*3*4*5*.n(n!(n!=0))办法:办法:1、迭代法、迭代法 2、函数递归、函数递归解题关键:解题关键:1、迭代公式、迭代公式 sum=sum*t;t=.2、迭代初值迭代初值sum=t=3、迭代次数迭代次数循环退出条件循环退出条件n!=1 (n=0,1)2n*(n-1)!(n1)分析返回第27页第27页函数递归调用例题函数递归调用例题()long fac(int n)if (n=1)return 1;else return n*fac(n-1);main()long nf;nf=fac(4);printf(“4!=%ldn,nf);求求fac(4)4*
19、f(3)nf=24求求fac(3)3*f(2)6求求fac(2)2*f(1)2 求求fac(1)1 返回值1fac(4)fac(3)fac(2)fac(1)返回返回第28页第28页 变量存储属性变量存储属性变量是对程序中数据存储空间抽象变量是对程序中数据存储空间抽象(变量名、变量值、变量类型、变量地址,变量名、变量值、变量类型、变量地址,变量存储属性变量存储属性)内存.main()int a;a=10;printf(“%d”,a);编译或函数调用时为其分派内存单元编译或函数调用时为其分派内存单元(静态存储器、动态存储器、存储器)(静态存储器、动态存储器、存储器)10程序中使用变量名对内存操作第
20、29页第29页存储属性涉及内容:存储属性涉及内容:变量可用域永久:程序执行整个过程 (编译时即建立)变量存储位置动态:仅在某一段时间内有效 (函数执行时建立,执行后释放,例形参)主存(静态,动态)/存储器变量生存期全局/局部第30页第30页【存储属性分类】【存储属性分类】存储属性registerregisterautoautostaticstaticexternextern存储位置存储器动态主存动态主存静态主存静态主存静态主存静态主存生存期生存期动态动态动态动态永久永久永久永久作用域作用域局部局部局部局部局部或全局局部或全局全局全局变量定义格式变量定义格式:存储类型存储类型 数据类型数据类型
21、变量表变量表;如:int sum;/*auto*/auto int a,b,c;register int i;static float x,y;第31页第31页动态变量动态变量1、自动变量(、自动变量(auto):函数被执行时自动建立函数被执行时自动建立,执行,执行 完自动撤消完自动撤消int i,j;(变量定义在函数内变量定义在函数内)auto int i,j;生存期:生存期:其所在函数执行时存在其所在函数执行时存在存储空间:存储空间:内存动态存储区内存动态存储区作用域:作用域:仅在定义它局部有效仅在定义它局部有效尤其提醒:main中定义auto变量只在main中有效形参属于auto变量不同
22、函数(调用)中同名auto变量,占不同内存单元定义在复合语句中变量,若复合语句中变量和其外层变量同名,则复合语句中变量屏蔽外层变量赋值前其值不确定,在所在函数被调用时进行初始化float f1(int a)int b,c;.char f2(int x,int y)int i,j;main()int m,n;.a,b,c有效x,y,i,j有效m,n有效第32页第32页例 不同函数中同名变量main()int a,b;a=3;b=4;printf(main:a=%d,b=%dn,a,b);sub();printf(main:a=%d,b=%dn,a,b);sub()int a,b;a=6;b=7;
23、printf(sub:a=%d,b=%dn,a,b);运营结果:main:a=3,b=4sub:a=6,b=7main:a=3,b=4运营结果:5 4 3 2 1例 复合语句中变量1#define N 5main()int i;int aN=1,2,3,4,5;for(i=0;iN/2;i+)int temp;temp=ai;ai=aN-i-1;aN-i-1=temp;for(i=0;iN;i+)printf(%d ,ai);第33页第33页例例:main()int x=1;void prt(void);int x=3;prt();printf(“%dn”,x);printf(“%dn”,x)
24、;void prt(void)int x=5;printf(“%dn”,x);x作用域运营结果运营结果5 53 31 1假如分程序中假如分程序中没有没有x结果会结果会如何?如何?511第34页第34页例例:main()int i=1;int count(int n);for(i=1;i=3;i+)count(i);int count(int n)int x;printf(“%d:x=%d,”,n,x);x+=2;printf(“x+2=%dn”,x);重点观测子函数重点观测子函数中中X值改变值改变运营结果运营结果1:X=3,X+2=52:X=-5,X+2=-33:X=1,X+2=3第35页第3
25、5页例例:main()int i=1;int count(int n);for(i=1;i=3;i+)count(i);int count(int n)int x=0;printf(“%d:x=%d,”,n,x);x+=2;printf(“x+2=%dn”,x);重点观测子函数重点观测子函数中中X值改变值改变运营结果运营结果1:X=0,X+2=22:X=0,X+2=23:X=0,X+2=2第36页第36页2 2、存储器变量(、存储器变量(register):register):存储器变量性质与自动变量基本一致存储器变量性质与自动变量基本一致存储器变量存取速度高存储器变量存取速度高将使用频率高变
26、量定义为该类型将使用频率高变量定义为该类型注意所用机器内部结构(存储器数量)注意所用机器内部结构(存储器数量)registerregister int i,j;第37页第37页静态变量静态变量 static静态变量未赋值时,默认为静态变量未赋值时,默认为0 0或或00尤其提醒:尤其提醒:子函数中静态变量为局部变量,其值含有继承性子函数中静态变量为局部变量,其值含有继承性static int i,j;存储空间:内存静态存储区存储空间:内存静态存储区生存期:编译时被建立,整个程序执行期间都存在生存期:编译时被建立,整个程序执行期间都存在作用域:可局部或全局作用域:可局部或全局(取决于该变量定义在函
27、数内取决于该变量定义在函数内还是函数外还是函数外)分为静态局部变量和静态全局变量两种)分为静态局部变量和静态全局变量两种第38页第38页例例:main()void count(void);for(i=1;i=3;i+)count();void count(void)int x=0;/*auto*/x+;printf(“%dn”,x);重点观测子函数重点观测子函数中中X值改变值改变运营结果运营结果111初始化在函数被调用时,初始化在函数被调用时,重复执行,重复初始化。重复执行,重复初始化。X为动态,因此其值不为动态,因此其值不含有继承性。含有继承性。第39页第39页例例:main()void c
28、ount(void);for(i=1;i第41页第41页float max,min;float average(float array,int n)int i;float sum=array0;max=min=array0;for(i=1;imax)max=arrayi;else if(arrayiy?x:y;return(z);main()extern a,b;printf(max=%dn,max(a,b);printf(“min=%dn”,min(a,b);int a=13,b=-8;运营结果:max=13 min=-8/*2.c*/extern a,b;int min()int z;z=
29、ab?a:b;return(c);main()int a=8;printf(max=%d,max(a,b);例 外部变量与局部变量运营结果:max=8返回第45页第45页int i;main()void prt();for(i=0;i5;i+)prt();void prt()for(i=0;i(y)?(x):(y).main()int a,b,c,d,t;.t=MAX(a+b,c+d);宏展开:t=(a+b)(c+d)?(a+b):(c+d);int max(int x,int y)return(xy?x:y);main()int a,b,c,d,t;.t=max(a+b,c+d);例 用宏定
30、义和函数实现同样功效第52页第52页带参宏与函数区别带参宏函数处理过程不分派内存简朴字符置换分派内存先求实参值,再代入形参处理时间编译时程序运营时参数类型无类型问题定义实参,形参类型程序长度变长不变运营速度不占运营时间调用和返回占时间第53页第53页7.2 文献包括功效:一个源文献可将另一个源文献内容所有包括进来普通形式:#include “文献名”或#include#include “file2.c”file1.cfile2.cfile1.cfile2.cABA处理过程:预编译时,用被包括文献内容取代该预处理命令,再对“包括”后文献作一个源文献编译 直接按原则目录搜索“”先在当前目录搜索,再
31、搜索原则目录可指定路径第54页第54页被包括文献内容源文献(*.c)头文献(*.h)宏定义数据结构定义函数阐明等文献包括可嵌套#include “file2.c”file1.cAfile3.cC#include “file3.c”file2.c Bfile1.cAfile3.cfile2.c第55页第55页思思考考file1.c,file2.c,file3.c,若若file1包括包括file2,file2又用到又用到file3内容,该如何实现?内容,该如何实现?file1.c#include “file2.c”file2.c#include “file3.c”file1.c#include “
32、file3.c”#include “file2.c”办法办法1办法办法2注:编译时,注:编译时,file1,file2,file3作为一个整体文献编译作为一个整体文献编译 任一函数外部变量不用再作申明任一函数外部变量不用再作申明第56页第56页例 文献包括举例/*powers.h */#define sqr(x)(x)*(x)#define cube(x)(x)*(x)*(x)#define quad(x)(x)*(x)*(x)*(x)/*ch8_10.c*/#include#include d:fengyibkcpowers.h#define MAX_POWER 10void main()int n;printf(numbert exp2t exp3t exp4n);printf(-t-t-t-n);for(n=1;n=MAX_POWER;n+)printf(%2dt%3dt%4dt%5dn,n,sqr(n),cube(n),quad(n);第57页第57页