资源描述
,单击此处编辑母版标题样式,*,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,第,8,章 函数,1,主要内容:,1,、函数的定义,2,、函数的调用(包括嵌套调用和递归调用),4,、局部变量和全局变量,5,、变量的存储类别,重点,:,1),如何定义函数(即如何编写函数),2),如何调用函数(包括库函数和自定义函数),2,函数的引入,函数:函数是完成某些特定功能的代码块。,使用函数的优点:,1,)实现模块化设计:将一个大任务分解成一个个的小任务,然后每个任务分别用函数实现。,2,)实现“编写一次,多次调用”,避免在不同的程序中重复编写相同的函数。,3,)便于程序调试和维护,因为每个函数之间是相互独立的。,函数的分类:库函数和用户自定义函数,3,8.1,函数的定义,例,8.1,编写函数,max,:求两个整数,x,和,y,中的较大数。,int max(int x,int y),/*,函数头*,/,int z=x;,/*,函数体,*/,if(xb?a:b);,或,int max(a,b),int a,b;,/*,形参的类型在函数体前、函数名后说明*,/,return(ab?a:b);,前者为标准格式,后者为传统格式,通常用前者。,7,4.,函数体,:,即,函数功能的具体实现。,它包括两部分:,说明部分和执行部分,,其中说明部分包括函数中所用的局部变量等的说明、函数中要调用的函数的说明。,注意:,函数不能嵌套定义,即函数内不能再定义函数,这样可以保证函数间是相互独立的,以实现模块化程序设计。,5.,空函数:,函数类型 函数名,(),调用此函数时,什么也不做。只是表明这里要 调用一个函数,而现在这个函数的功能还没实现。,8,空函数在程 序设计中常常用到的:,1,)预留函数,便于以后扩充程序功能。,2,)便于程序的模块化设计和调试:程序设计中往往根据需要确定若干模块,分别由一些函数来实现。一个大系统,需要编写很多用户函数,而这些函数不可能 也没有必要同步完成,通常足从一些基本模块开始,编写一个调试一个,对于没有编写 的函数就需要用空函数代替。从而也有利于集体创作。,9,空函数举例,例,8.3,编写小学生算术练习系统的主程序:显示主菜单,用户选择,根据选择执行加、减、乘、除、退出,5,项功能之一。,重复上述步骤,直至选择退出。,其中主程序调用的函数有:显示主菜单函数,list_menu(),加、减、乘、除、退出函数分别是,add(),sub(),mul(),divide(),end().,以上函数除,list_menu(),外此时均为空函数。,程序:,l8_1_4.c,10,#include stdio.h /*l8_1_4.c*/,main(),void add(),sub(),mul(),divide(),end(),list_menu(),;int n;,do,list_menu(),;,scanf(%d,switch(n),case 1:,add();,break;,case 2:,sub(),;break;,case 3:,mul();,break;,case 4:,divide(),;break;,case 5:,end(),;break;,default:printf(n enter error,please again.);,while(n!=5);,11,void list_menu(),printf(n*the exercise system for primitive students*);,printf(n*1.add *);,printf(n*2.sub *);,printf(n*3.mul *);,printf(n*4.divide*);,printf(n*5.end *n);,void add(),void sub(),void mul(),void divide(),void end(),12,问题:,如何定义一个函数,第一步:,分析函数需要的参数,包括参数的的个数以及每个参数的类型,第二步:,分析函数返回值的类型,若无返回值,则为,void,。,函数的返回值可看作是函数执行完后需输出的,一个,数据。,第三步:,编写函数体,说明:,参数和返回值是函数之间的接口,即函数之间通过参数和返回值进行通信。参数包括执行该函数时需要的数据信息,以及返回数据的有关信息。,13,例如:,1,),求,n!:,要处理的数据是,n,,因此必须有一个参数,n,,类型为,int,。返回值为,long,型。即,long fact(int n),2,),打印表头,:,不需输入任何数据即可执行该函数,因此无参数。执行该函数无返回值,因此函数类型为,void,。即,void line(),3,)求两个整数,m,和,n,的最小公倍数,执行该功能时必须有两个整型参数,返回值为整型。程序:,l8_1_5.c,int min_multiple(int m,int n),(见下页),4,)求一批整型数据(,n,个)中的最大值。实现该功能的函数的参数有两个:该批数据的首地址及数据的个数。返回值为一个整型数。,int max(int data,int n),14,/*,例,3:,计算两个整数的最小公倍数,l8_1_5.c*/,main(),int m,n,min;,int min_multiple(int,int);/*,函数声明*,/,printf(n input m,n:);,scanf(%d%d,min=,min_multiple(m,n);/*,函数调用*,/,printf(n bei shu:%d,min);,int min_multiple(int x,int y)/*,函数定义*,/,int i;,i=1;,while(x*i%y!=0)i+;,return(x*i);,15,8.2,函数的调用,重点,:,1,、对被调函数的声明,2,、如何调用一个函数,3,、主调函数和被调函数之间如何进行数据传递,例,8.5,调用函数,fact(),求,n!(n,由用户输入)。,分三种情况:,(1),函数,fact(),与主函数在同一文件中,且,main(),在,fact(),前面。,(2),函数,fact(),与主函数在同一文件中,且,main,在,fact,之后。,(3),函数,fact,与,main,不再同一程序文件中,。,16,main()/*(1),main(),在,fact(),前面,*,/,int n;long p;,long fact(int);,/*,函数声明*,/,scanf(%d,p=,fact(n);,/*,函数调用*,/,printf(n%ld,p);,long fact(int m),/*,函数定义*,/,int i;long s=1;,for(i=1;i=m;i+),s*=i;,return(s);,/*,函数返回*,/,结论,:,被调函数在后,需在主调函数中先声明后调用。,17,/*(2),主调函数在被调函数之后*,/,long fact(int m),/*,函数定义*,/,int i;long s=1;,for(i=1;i=m;i+),s*=i;,return(s);,main(),int n;long p;/*,不需函数声明*,/,scanf(%d,p=,fact(n);,/*,函数调用*,/,printf(n%ld,p);,结论,:被调函数先于主调函数被编译,因此在编译主调函数时已知被调函数的类型等信息。故不需函数声明,。,18,/*(3),设,fact(),函数存放在文件,f1.c,中,则编写主调函数时需用,include,命令对被调函数声明*,/,#include ,main(),int n;long p;,scanf(%d,p=fact(n);,printf(n%ld,p);,结论:该程序的效果与(,2,)相同,由此可,实现将多个文件连接成一个程序。,同理,,对库函数的调用都要在,main,函数前用,include,命令将函数所在的头文件包含进来。,19,函数声明小结:,被调函数必须是已存在的函数,通过“函数声明”告知编译系统关于被调函数的有关信息。,函数声明的形式:,函数类型 函数名(参数表);,(注意与函数定义的区别),1,、若被调函数是库函数或用户已编写的函数(与主调函数不在同一文件中),则使用前需在程序的开头用,include,命令将被调函数的信息包含进来。,2,、若主调函数与被调函数在同一文件内,且主调函数在前,则必须在主调函数的说明部分或主调函数的前面对被调函数进行说明。,3,、以下情况下可以省略对被调函数的说明。,a),函数类型为整型,b),被调函数在主调函数之前定义,通常,将所有函数的说明集中在程序开头;或将所有函数的信息写入一个文件,编程时用,include,命令将其包含进来即可。,20,函数的调用与返回过程小结,1,、函数调用的一般形式:,函数名,(,实参表,),如,p=,fact(n),;,printf(“%d”,power(2,n),);,等,注意,:,实参与形参的类型、个数、顺序必须一致,。,2,、,调用过程:,1,),在调用函数时,首先将实参的值赋给形参;再将控制流程转到被调函数;,2,)然后执行被调函数。,3,)当被调函数执 行到,return,语句,或执行到被调函数函数体最后的一个大花括号时,控制流程返回到 主调函数的断点处继续执行主调函数。如果被调函数有返回值,则控制流程返回的同时将该返回值带回主调函数。,21,3,、函数的返回,函数返回的实现,:,1,)函数体中通过执行,return,语句返回,其格式有,3,种:,return(expression,),;,或,return expression;,或,return;,2),若函数体中无,return,语句,当执行到函数末尾时自动返回到调用函数。,注意:,1),函数的返回值最多只有一个,,可通过,return,语句返回主调函数。,2),当有,多个值,需要返回主调函数时,用,return,语句无法实现,只能通过,传地址,调用实现。如对数组元素排序等。,22,4.,参数传递:实参与形参的结合,形参,:,定义函数时的参数为形参,此时的参数无具体的值,仅仅表示参数的类型、个数、以及在函数体内对其如何处理,。,其作用是:该函数被调用时用来接收实参的值,.,实参,;,调用函数时的参数为实参,它表示该函数要处理的数据的信息,因此实参必须有确定的值。调用时,将实参的值传给形参,。,要求,:,调用函数时,实参与形参的类型、个数必须完全一致。,分析例,8.4,程序的调用过程:明确实参与形参的作用。(传值调用:单向传递),23,例,8.4,以下程序企图通过调用,swap,函数,交换主函数中变量,x,和,y,中的数据。请观察程序的输出结果。,void swap(int a,int b),int t;,printf(2)a=%d b=%dn,a,b);,t=a;a=b;b=t;,printf(3)a=%d b=%dn,a,b);,main(),int x=10,y=20;,printf(1)x=%d y=%dn,x,y);,swap(x,y);,printf(4)x=%d y=%dn,x,y);,24,程序运行结果如下:,(1)x=10 y=20,(2)a=10 b=20,(3)a=20 b=10,(4)x=10 y=20,结论:,参数的传递是单,向的,即只能由实参传,给形参,在被调函数中,对形参的改变的不影响,实参的值。,25,例,8.12,编写函数实现:用选择法对,n,个整数排序。编写主函数实现数据的输入输出,。,分析:,主程序的算法,:,S1,:输入一批数据,(,个数为,N),,存入一维数组,aa,。,S2,:调用函数,sort(),,对一维数组中的数据按从小到大的顺序排序。,S3,:输出数组,aa,中的各元素。,函数,sort(),的编写,:,首先,确定函数的类型:,void,;,参数:一维数组的首地址,数据的个数,共,2,个;,然后编写函数体,实现排序。,重点:分析参数的传递,比较传值与传地址的区别,各有何用处?,(图示,:,形参数组与实参数组的结合方式),26,/*,主函数*,/,#define N 10,main(),int aaN,i;,void sort(int b,int n,);,printf(n enter integers for sort;);,for(i=0;iN;i+),scanf(%d,sort(aa,N);,/*,函数调用*,/,printf(n after sort:n);,for(i=0;iN;i+),printf(%6d,aai);,/*,函数,sort()*/,void sort(int b,int n),int i,j,t,k;,for(i=1;i=n-1;i+),k=0;,for(j=1;j=n-i;j+),if(bk=6),even(a);,/*,函数调用语句*,/,else,printf(The%d isnt even numbern,a);,32,void even(int x)/*,函数定义*,/,int i;,for(i=2;i=x/2;i+),if(isprime(i),if(isprime(x-i),printf(%d=%d+%dn,x,i,x-i);return;,int isprime(int a)/*,函数定义*,/,int i,k=sqrt(a);,for(i=2;i1)-,规律,f(1)=1 (n=1)-,递归结束条件,程序:,l8_4_1.c,分析程序的执行过程,理解递归中的“递进”与“回推”。,34,main()/*,程序:,L8_4_1.c*/,int n;long p;,long f(int);,/*,对被调函数的声明*,/,printf(n input n:);,scanf(%d,p=,f(n),;/*,函数调用*,/,printf(n n!=%ld,p);,long f(int m),/*,函数定义*,/,long t;,if(m=1)t=1;,else t=m*,f(m-1),;/*,函数递归调用*,/,return(t);/*,函数的返回*,/,35,类似的递归问题:,1,。猴子吃桃:,int peach(int day),int n;,if(d=10)n=1;,else n=2*(peach(day+1)+1);,return n;,2,。猜年龄:第一个人说它比第二个人大,4,岁,第二个人说它比第三个人大,4,岁,第三个人说它比第四个人大四岁,第四个人,10,岁,问:第一个人多大?,36,8.5,局部变量和全局变量,指变量的作用范围不同。,局部变量:在函数体内定义的变量。,作用范围:只在本函数内有效,如前面例题中的变量、数组等。,全局变量:在函数体外定义的变量,作用范围:从定义该变量的位置开始,到本源程序文件结束。,37,程序:,/*,全局变量,x,y*/,int x=100;float y=66.6;,f1(),float y=0,;/*,局部变量,y*/,printf(“x=%dt”,x);,printf(“y=%ft”,y);,int z=1;,f2(),int i;,/*,局部变量,i*/,for(i=1;i5;i+)putchar(*);,printf(n z=%dt,z);,/*,全局变量,z,从 定义起至程序末起作用*,/,main(),f1();f2();,printf(“y=%fn”,y);,/*,输出全局变量,y*/,运行结果:,x=100 y=0.000000 *,z=1 y=66.600000,/*,全局变量,x,y,在本程序内起作用*,/,/*,局部变量,y,的作用范围内,全局变量,y,不作用*,/,38,int d=1;,/*,全局变量,d*/,fun(int p),int d=5;,/*,局部变量,d*/,d+=p+;/*,使用局部变量,d*/,printf(%d,d);,main(),int a=3;,fun(a);,d+=a+;/*,使用全局变量,d*/,printf(%d,d);,运行结果:,84,结论:在同一源程序文件中,若外部变量与局部变量同名,则在局部变量的作用范围内,外部变量不起作用。,39,局部变量和全局变量小结,局部变量:保证了函数之间的独立性。(常用),全局变量:增加了函数之间数据传递的通道,但降低了函数间的独立性,降低了程序的清晰性,因此副作用太大。除非特别需要时,一般不用。,占用内存情况:,局部变量,仅当他所在的函数被调用时才存在,执行完该函数返回后,该变量不再存在,全局变量,在程序的全部执行过程中一直存在,直至程序执行完,才释放它所占的内存空间,40,8.6,变量的存储类别,变量和函数均有两个属性:,数据类型和存储类别,存储类别指数据在内存中的存储方式。根据变量的“生存期”不同,变量的存储类别包含以下四种:,自动的,:auto,静态的,:static,寄存器的,:register,外部的,:extern,41,自动的(,auto,)变量,函数的形参和在函数中定义的变量(通常省略存储类别,)即隐含指定为自动变量。前面,17,章中的变量均属自动变量。,自动变量在需要时系统给他门分配存储空间,在函数调用结束时自动释放这些存储空间。,例,auto int a=2,b=3;,与,int a=2,b=3;,等价。,42,静态的(,static,),1,、静态局部变量:,作用域为本函数内部,存储类别为静态存储类,因此其生存期与该函数所在程序运行期间相同。即当函数调用结束时能保留原值,在下一次调用该函数时该变量的值是上一次函数调用结束时的值,直至程序运行结束。,例,(next page),43,func(int a,int b)/*,例,8.6.1*/,static int m=0,i=2,;/*,静态局部变量,m,I*/,i+=m+1;,m=i+a+b;,return(m);,main(),int k=4,m=1,p;,/*,局部变量,k,m,p*/,p=func(k,m);,printf(%5d,p);,p=func(k,m);,printf(%5dn,p);,运行结果:,8 17,/*,图示执行过程中变量的存储空间占用及值的变化情况*,/,44,int d=1;,/*,全局变量,d*/*,例,8.6.2*/,fun(int p),static int d=5;,/*,静态局部变量,d*/,d+=p;,printf(%5d,d);,return(d);,main(),int a=3;,/*,自动变量,a*/,printf(%5dn,fun(a+fun(d);,运行结果:,6 15 15,45,Register,变量,register int k;,则给变量,k,分配的空间为某个寄存器。,优点:速度快。,只有局部变量和形参可以定义为,register,变量。因为机器的寄存器数量有限,因此该类型不常用。,46,外部的(,extern,)变量,用,extern,声明外部变量,是为了扩展外部变量的作用域。,因外部变量不常用,因此,extern,也很少使用。,必须使用外部变量时,一般建议使用静态全局变量。即在函数体外定义变量时存储类别为:,static,如,static int a=2;,f(),变量,a,的作用范围仅限于本源程序文件内。其它程序中即使用,extern int a;,声明也不能引用,a,。,47,8.7,内部函数与外部函数,外部函数:如不加特别说明,函数都是全局的,即外部的,一个函数可以调用另一文件中的函数。(项目文件的使用,),内部函数:存储类别为,static,,该函数仅限于本程序文件使用,其它程序不能,调用之。,48,综合题,1,编程实现小学生算术练习系统:主菜单包括,5,项(加法、减法、乘法、除法、退出),前四项中每一项又包括子菜单(一级、二级、三级、返回,4,项),其中一级实现,10,以内的运算,二级实现,50,以内的整数运算,三级实现,100,以内的整数运算;进入某一级后,反复练习(由机器产生两个随机数,用户输入运算结果,输出正确或错误)待结束时给出本级题目中计算正确的百分比。,要求每个功能分别用函数实现。程序:,l8_t.c,49,综合题,2,自动阅卷程序,:,设单选题,20,个(,2,分,/,题),多选题,20,个(每题,4,个供选答案,,3,分,/,题,且只要与答案不一致即错)。编写函数实现:阅单选题,阅多选题,阅一个人的答题;编写主函数实现批阅,N,个人的答题卡(正确答案在主程序中输入)。,分析:,Main(),:,1),输入正确答案,分别存入一维数组,dd,二维数组,ss;2),批阅,N,个人的答题(用循环),将成绩存入数组,sc,。,3,)输出每个人的最后成绩。,Person():,函数类型为,int;,参数两个:,dd,ss;,函数体:,1,)输入某人的答案,分别存入,dd1,和,ss1,,,2,)分别调用函数,single(),和,many(),判别对错并计分,返回总成绩。,50,Single():,函数类型,int,参数,dd,dd1,;函数体:统计数组,dd,与,dd1,中相同元素的个数,乘,2,即得单选题的成绩,返回该成绩。,Many():,函数类型,int,参数,ss,ss1;,函数体:统计数组,ss,与数组,ss1,中对应行元素相同的行数,乘,3,即得多选题的成绩,并返回。,然后分析各函数的具体实现算法。,程序,1,:,l8_3_2.c,分析程序中的传地址调用,嵌套调用时程序的执行过程。,程序,2,:,l8_3_2_q.c,定义存放正确答案的数组为全局的,则,dd,ss,将不作为参数使用。(一般情况使用参数传递),51,#include stdio.h /*,程序,1*/,#define N 400 /*num of person*/,#define NUM 20 /*num of question*/,main(),char ddNUM+1,ssNUM+15;,int scN+1,i;,printf(n enter right answer of single select:n);,printf(n(format:press enter after finished inputing all of answers.n);,for(i=1;i=NUM;i+),ddi=getchar();,getchar();,52,printf(n enter answer of multi select:n);,printf(nformat:(press enter after inputing a question)n);,for(i=1;i=NUM;i+),gets(ssi);,for(i=1;i=N;i+),sci=person(dd,ss);,printf(n no:score:);,for(i=1;i=N;i+),printf(n%-6d%-6d,i,sci);,53,int person(char dd,char ss5),char dd121,ss1315;,int s,i;int signle(),many();,printf(n enter answer of single select:);,for(i=1;i=NUM;i+),dd1i=getchar();,getchar();,printf(n enter answer of multi select:);,for(i=1;i=NUM;i+),gets(ss1i);,s=single(dd,dd1)+many(ss,ss1);,return(s);,54,int single(char dd,char dd1),int n=0,i;,for(i=1;i=NUM;i+),if(ddi=dd1i)n+;,return(2*n);,int many(char ss 5,char ss1 5),int n=0,i;,for(i=1;i1,时,计算函数值的过程中又调用函数,pp,,即递归调用。参数的变化规律为:,n-1,n-2,。即,if(n1)h=2*x*pp(n-1,x)-2*(n-1)*pp(n-2,x),递归结束的条件为:,n,等于,0,或,n,等于,1,程序:,x8_13.c,60,通过参数在函数间传递数据,X8.14,分析:,主函数调用,4,个函数,设函数名分别为,average1,average2,max_sc,jun.,参数分别为:,void average1(float s5,float a),void average(float s5),void max_sc(float s5),void jun(float a),分别实现的功能为:,61,
展开阅读全文