版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
0102037.1函数概述7.2函数的定义7.3函数的调用
函数04057.4函数的声明7.5数组作为函数参数0607087.6变量的作用域7.7变量的生命周期7.8内部函数和外部函数09107.9知识点强化与应用7.10本章小结【学习要点】1.函数的定义2.函数的调用、说明3.局部变量和全局变量本章主要介绍函数定义和函数调用。希望大家能通过学习建立起函数构建系统功能模块的思想。定义学生信息管理系统中的各个子模块的功能,并当用户输入需要进行的操作序号(1-5)后,程序执行相对应的功能,如录入学生信息,通过学号查找学生信息,统计三门课程平均分,统计总分最高分,退出等操作。【导入案例:利用函数构建起系统功能模块的思想】如果采用前面章节所讲的顺序、分支、循环结构的知识来逐个完成学生信息的录入,查找,统计等系统功能,合理吗?由于用户下一步进行什么操作是无法预知的,用三大结构写起来不仅冗余繁琐,而且逻辑结构非常混乱,那么程序如何实现对系统功能模块的结构设计呢?分析前面的章节中我们讲过,当需要表达程序某一些特定功能时,可以采用将此程序段以花括号括起的形式,但是如果程序中需多次使用该功能,代码便会重复出现,程序变得繁琐冗余。在本系统中,用户后续每一步操作都存在着各种可能性,如果把所有的可能性逐一排列出来,程序的可读性会大大降低,解决这一问题的最好方法就是函数。【解决方案】定义四个函数分别完成学生信息的录入,查找,统计,退出,通过主函数的指引来调用以上函数。分析函数是C语言源程序的基本模块,函数有三个作用:任务划分,代码重用,信息隐藏。7.1函数概述7.1函数概述(续)C语言是非常灵活的语言,它提供了极为丰富的库函数和自定义函数,库函数如printf()函数、scanf()函数。自定义函数是用户将某个算法编成一个相对独立的函数模块,然后以调用的方式来使用。C程序又被称为函数式语言。由于采用了函数模块式的结构,C语言易于实现结构化程序设计。使程序的层次结构清晰,便于程序的编写、阅读、调试。库函数库函数由C语言提供。调用库函数之前必须使用"include"包含对应的"头文件",常见的头文件如下所示。输入输出函数:#include<stdio.h>数学函数:#include<math.h>字符函数:#include<ctype.h>字符串函数:#include<string.h>杂项函数及内存分配函数:#include<stdlib.h>自定义函数自定义函数由用户编写,使用方法同库函数类似,区别在于使用前不需要使用“include”包含对应的“头文件”。但一般情况下需要对函数进行声明。7.2.1函数的定义7.2.2函数的参数7.2.3函数的返回值7.2函数的定义7.2.1函数的定义函数包括函数的首部和函数体两个部分,函数返回值类型、函数名、参数以及参数的类型构成了函数的首部,而函数体主要是由实现函数功能的语句构成。7.2.1函数的定义(续)例:intmax(inta,intb)//函数的首部{//花括号括起的部分为函数体if(a>b)returna;elsereturnb;}7.2.1函数的定义(续)另例(请对比)intmax(inta,intb){if(a>b)printf(“%d”,a);elseprintf(“%d”,b);}思考:自定义函数功能设计单一有何好处?7.2.2函数的参数函数分为有参函数和无参函数两种类型。在实际编程时要根据程序的需求做出不同的设计,是否带参数并不是评判一个函数优劣的依据,在定义某个函数时,参数的意义是主调函数通过参数将需要处理的数据传递给被调函数进行处理。7.2.2函数的参数(续)无参函数的定义形式:函数返回值的类型函数名(){//函数体}7.2.2函数的参数(续)【例7-1】在屏幕上输出如下图形7.2.2函数的参数(续)分析:对于这道题仅仅采用前面学习的知识可以这么编写:#include<stdio.h>voidmain(){printf(“*********************\n”);printf(“*********************\n”);printf(“HelloWorld\n”);printf(“*********************\n”);printf(“*********************\n”);}7.2.2函数的参数(续)若采用函数的思想来编写则程序的可读性和扩展性更强。分析:编写一个star()函数和一个str()函数分别完成星号和字符串的输出功能,然后在主函数中调用这些函数来完成题目需求。7.2.2函数的参数(续)【例7-2】输出星号和字符的组合图形#include<stdio.h>voidstar(){printf(“*********************\n”);}voidstr(){printf(“HelloWorld\n”);}intmain(){star();star();str();star();star();return0;}7.2.2函数的参数(续)有参函数的定义形式:函数返回值的类型
函数名(类型形参1,类型形参2,…){//函数体}7.2.2函数的参数(续)注意:用户自定义的函数本身不能直接被编译执行,下面将本章的第一个小例题直接运行,结果编译出错!7.2.2函数的参数(续)【例7-3】比较两个数的大小#include<stdio.h>intmax(inta,intb){if(a>b)returna;elsereturnb;}voidmain(){
}
此题编译和连接都不报错,但是没有任何结果。下面在简短学习函数返回值以后,我们将正式开始学习如何正确的调用自定义函数。7.2.3函数的返回值函数的返回值要根据函数完成的功能来设定,是否需要返回值并没有统一的要求。函数的返回值是指函数被调用以后,被调函数将所取得的并返回给主调函数的值。7.2.3函数的返回值(续)(1)函数的返回值只能通过return语句获取,并返回给主调函数。一般形式如下:return表达式;或return(表达式);该语句的功能是计算表达式的值,并返回给主调函数。在函数中允许有多个return语句,但每次调用只会有一个return语句被执行,因此函数只能返回一个值。7.2.3函数的返回值(续)(2)函数在首部定义中的返回值类型,应该和return后表达式或变量的类型保持一致。如果两者不一致,则以函数首部中定义的类型为准,自动进行类型转换。如intfun(floata){returna;}返回值为整型而不是浮点类型。7.2.3函数的返回值(续)(3)如函数返回值为整型,在函数定义时可以略去类型说明。如fun(floata){returna;}返回值仍然是整型。7.2.3函数的返回值(续)(4)不需要有返回值的函数,可以明确定义为“空类型”,类型说明符为“void”。一旦函数被定义为void类型后,就不能在主调函数中使用被调函数的函数值。如,在定义s为空类型后,在主函数中写下述语句:sum=s(n);//错误说明:为了使程序有良好的可读性并减少出错,凡不要求返回值的函数都应定义为空类型。应注意,无论函数的返回类型是否为空,函数在调用执行完子函数体后都会返回到主调函数中。7.3.1函数调用的一般形式7.3.2函数的嵌套调用7.3.3函数的递归7.3函数调用的一般形式7.3.1函数调用的一般形式在C程序中是通过对函数的调用来执行各函数的函数体。函数调用的一般形式为:无参函数的调用:函数名();有参函数的调用:函数名(实参列表);1)调用函数时,函数名必须与所调用的函数名字完全一致。2)实参的个数、类型必须与形参的个数、类型一一对应。如果类型不匹配,程序将自动转换。3)函数必须先定义,后调用(函数的返回值类型为int或char时除外)。4)函数直接或间接地调用自己本身,称为递归调用。7.3.1函数调用的一般形式(续)被调函数a(){函数体;}主调函数b(){语句1;a();//调用函数a语句2;语句3;}①③中断②④⑤图7-3函数调用示意图函数调用示意图7.3.1函数调用的一般形式(续)【例7-4】从键盘上输入五组数,两两比较大小后将较大值返回给主函数输出。#include<stdio.h>intmax(inta,intb){ if(a>b)returna; elsereturnb;}voidmain(){ intx,y,i=1; while(i<5) { printf("请输入两个数求较大值,数字之间用空格隔开,结束输入请按下回车键\n"); scanf("%d%d",&x,&y); printf("%d和%d之间较大值是%d\n",x,y,max(x,y)); i++; }}7.3.1函数调用的一般形式(续)【例7-5】判断3-1000之间的数字是否是素数的程序,如果是素数将其输出。#include<stdio.h>intisprime(intvalue){ intk; for(k=2;k<value;k++) { if(value%k==0)break; } if(value==k)return1; elsereturn0;}voidmain(){ inti; for(i=3;i<1000;i++) { if(isprime(i)==1) printf("%d\t",i); } }?思考:若要让程序的结果按照5个数为一行的形式输出,应该如何改写?7.3.1函数调用的一般形式(续)【例7-6】设计一个函数,其功能是交换两个数的值,注意观察输出函数。#include<stdio.h>voidexchange(intx,inty){ intz; printf("②我是被调函数中交换之前的参数%d,%d\n",x,y); {z=x;x=y;y=z;}//交换代码 printf("③我是被调函数中交换之后的参数%d,%d\n",x,y);}voidmain(){ inta=10,b=20; printf("①我是主调函数中调用之前的参数%d,%d\n",a,b); exchange(a,b); printf("④我是主调函数中调用之后的参数%d,%d\n",a,b);}7.3.2函数的嵌套调用C语言中函数不能嵌套定义,但可以嵌套调用。嵌套函数,就是指在某些情况下,在某个子函数的定义模块中需要去调用另外一个函数,这就是函数的嵌套调用。需要注意的是嵌套函数调用结束后逐层返回。参见函数嵌套调用示意图。7.3.2函数的嵌套调用(续)【例7-7】要求从键盘输入终止值,调用函数计算平方和,如输入5,则计算1到5的平方和。#include<stdio.h>intproduct(inti)//乘积函数{ return(i*i);}intsum(intend)//累和函数{ inti,s=0; for(i=1;i<=end;i++) { s+=product(i); } returns;}voidmain(){ intend,s; printf("请输入需要计算的终止值:\n"); scanf("%d",&end); s=sum(end); printf("1~%d的平方和是%d\n",end,s);}7.3.2函数的嵌套调用(续)同名参数在内存中的运行示意图7.3.3函数的递归C语言中的函数可以递归调用,即直接或间接地调用自己,一个问题要采用递归方法来解决时,必须符合以下3个条件:1)可以把要解决的问题转化为一个新的问题,而这个新问题的解决方法仍然与原来的解法相同,只是所处理的对象有规律地递增或者递减。2)可以应用这个转化过程使得问题得到解决。3)必定要有一个明确的结束递归条件。7.3.3函数的递归(续)【例7-8】简单的函数递归调用举例。#include<stdio.h>fun(intx){ if(x/2>1) fun(x/2);//调用自身 printf("%d",x);}main(){ fun(7); printf("\n");}递归函数运行示意图7.3.3函数的递归(续)【例7-9】递归举例:求n!,n由键盘输入。#include<stdio.h>intf(intn){ if(n==1||n==0) return1; else returnn*f(n-1);}voidmain(){ intn,s; printf("请输入整数n,计算n的阶乘:\n"); scanf("%d",&n); s=f(n); printf("%d的阶乘结果为%d\n",n,s);}除主函数外,用户定义的函数都要遵循“先定义、后使用”的规则,声明的方法很简单,只需要将函数的头部复制到被调用语句之前,然后再末尾加上分号即可,一般是将函数的声明放在预处理命令后面。注意声明是语句,末尾要加“;”分号。7.4函数的声明7.4函数的声明(续)【例7-10】改写本章第一个小例子#include<stdio.h>intmax(inta,intb)//被调函数{ if(a>b)returna; elsereturnb;}voidmain()//主函数,被调函数在主调函数的前面,所以无需声明{ intx,y; printf("请输入两个数求较大值,数字之间用空格隔开,结束输入请按下回车键\n"); scanf("%d%d",&x,&y); printf("%d和%d之间较大值是%d\n",x,y,max(x,y));}7.4函数的声明(续)【例7-11】[例7-10]的另一种编写方式。#include<stdio.h>intmax(inta,intb);//声明语句voidmain()//主函数,被调函数在主调函数的后面,所以必须声明{ intx,y; printf("请输入两个数求较大值,数字之间用空格隔开,结束输入请按下回车键\n"); scanf("%d%d",&x,&y); printf("%d和%d之间较大值是%d\n",x,y,max(x,y));}intmax(inta,intb)//函数定义{ if(a>b)returna; elsereturnb;by}7.5.1数组元素作为函数的实参7.5.2数组名作为函数实参7.5数组作为函数参数7.5.1数组元素作为函数的实参【例7-12】输入一组学生的分数,依次判断分数的等级。#include<stdio.h>voidIsPass(intscore){ if(score>=90)printf("恭喜,您的成绩等级为优秀\n"); elseif(score>=80)printf("恭喜,您的成绩等级为良好\n"); elseif(score>=70)printf("恭喜,您的成绩等级为中等\n"); elseif(score>=60)printf("恭喜,通过考试\n"); elseprintf("很遗憾,你挂科了\n");}voidmain(){ inta[5],i; printf("请输入分数判断等级:\n"); for(i=0;i<5;i++) { scanf("%d",&a[i]); IsPass(a[i]); } }数组元素即下标变量,与普通变量并无区别。因此它作为函数实参使用与普通变量是完全相同的,在发生函数调用时,把作为实参的数组元素的值传送给形参,实现单向的值传送。7.5.2数组名作为函数实参用数组名作函数参数与用数组元素作实参有2点不同。(1)用数组元素作实参时,仅要求作为下标变量的数组元素的类型和函数形参变量的类型一致。即,并不要求函数的形参也是下标变量。换句话说,对数组元素的处理是按普通变量对待的。用数组名作函数参数时,则要求形参和相对应的实参都必须是类型相同的数组,都必须有明确的数组说明。当形参和实参二者不一致时会发生错误。7.5.2数组名作为函数实参(续)(2)在普通变量或下标变量作函数参数时,形参变量和实参变量是由编译系统分配的两个不同的内存单元。在函数调用时发生的值传递是把实参变量的值赋予形参变量。在用数组名作函数参数时,不是进行值的传递,而是把实参数组的地址传递给形参数组,因为实际上形参数组并不存在,编译系统不再另外为形参数组分配内存。传递地址后,形参数组和实参数组在内存中指向同一段内存空间。数组内存示意图7.5.2数组名作为函数实参(续)【例7-13】依次输入10位学生的分数,计算平均成绩。#include<stdio.h>floataverage(intb[10]){ inti; floataver,sum=0; for(i=0;i<10;i++) sum=sum+b[i]; aver=sum/10; returnaver;}voidmain(){ inta[10],i; floataver; printf("\n请输入10位学生的分数,计算平均分\n"); for(i=0;i<10;i++) scanf("%d",&a[i]); aver=average(a); printf("平均分是%.2f\n",aver);
}在C语言中,程序的编译单位是程序文件,一个源文件可以包含一个或多个函数。按照作用域的范围可分为两种,即局部变量和全局变量。在函数内定义的变量是局部变量,在函数之外定义的变量称为外部变量,也就是全局变量。全局变量可以为源文件中其他函数所共用,其作用域为从定义变量的位置开始到源文件结束。7.6变量的作用域7.6变量的作用域(续)1.局部变量局部变量也称为内部变量。局部变量在函数内进行定义说明。其作用域仅限于函数内,函数外再使用这个变量是非法的。例:intfun(inta)//函数fun{intb,c;
}fun内定义了三个变量,a为形参,b,c为普通变量。在fun的范围内a,b,c有效,或者说a,b,c变量的作用域限于fun函数内部。7.6变量的作用域(续)关于局部变量的作用域给出以下几点说明:(1)主函数中定义的变量也只能在主函数中使用,不能在其它函数中使用。同时,主函数中不能使用其它函数中定义的变量。因为主函数也是一个函数,它与其它函数是平行关系。(2)形参变量是属于被调函数的局部变量,实参变量是属于主调函数的局部变量。(3)允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的内存单元,互不干扰,也不会发生混淆。7.6变量的作用域(续)2.全局变量全局变量也称为外部变量,它定义在函数的外部,不属于具体哪一个函数,而是属于一个源程序文件。其作用域是整个源程序。在函数中使用全局变量,一般应作全局变量说明。只有在函数内经过说明的全局变量才能使用。全局变量的说明符为extern。但在一个函数之前定义的全局变量,在该函数内使用可不再加以说明。例:inta,b;//外部变量voidfun()//函数fun{//语句;}7.6变量的作用域(续)从上例可以看出a、b是在函数外部定义的外部变量,即是全局变量。关于全局变量的作用域给出以下几点说明:(1)对于局部变量的定义和说明,可以不加区分。而对于外部变量则不然,外部变量的定义和外部变量的说明并不同。外部变量定义必须在所有的函数之外,且只能定义一次。其一般形式为:[extern]类型说明符变量名,变量名…其中方括号内的extern可以省去不写。例如:inta,b;等效于:
externinta,b;7.6变量的作用域(续)而外部变量的说明出现在要使用该外部变量的各个函数内,在整个程序内,可能出现多次,外部变量说明的一般形式为:extern类型说明符变量名,变量名,…;外部变量在定义时就已分配了内存单元,外部变量定义可作初始赋值,外部变量说明不能再赋初始值,只是表明在函数内要使用某外部变量。(2)外部变量可加强函数模块之间的数据联系,但是又使函数要依赖这些变量,因而使得函数的独立性降低。从模块化程序设计的观点来看这是不利的,因此在不必要时尽量少用全局变量。(3)在同一源文件中,允许全局变量和局部变量同名。在局部变量的作用域内,全局变量不起作用。7.6变量的作用域(续)【例7-14】多源文档工程中变量的定义。文档a.c代码如下。staticinti;//只在a文档中用intj;//在工程里用staticvoidfun()//只在a文档中用{ //语句;}voidcallme()//在工程中用{ staticintsum;}文档b.c代码如下。externintj;//调用a文档里的externvoidcallme();//调用a文档里的intmain(){ //语句;}变量的存储类型决定了各种变量的作用域不同。所谓存储类型是指变量占用内存空间的方式,也称为存储方式。变量的存储方式可分为“静态存储”和“动态存储”两种。7.7变量的生命周期7.7变量的生命周期(续)静态存储变量通常是在变量定义时就分配存储单元并一直保持不变,直至整个程序结束。动态存储变量是在程序执行过程中,使用它时才分配存储单元,使用完毕立即释放。典型的例子是函数的形式参数,在函数定义时并不给形参分配存储单元,只是在函数被调用时,才予以分配,调用函数完毕立即释放。如果一个函数被多次调用,则反复地分配、释放形参变量的存储单元。7.7变量的生命周期(续)存储类型说明关键字含义说明auto自动变量函数内未加存储类型说明的变量均视为自动变量register寄存器变量存放在CPU的寄存器中,使用时,不需要访问内存,而直接从寄存器中读写,提高效率extern外部变量外部变量和全局变量是对同一类变量的两种不同角度的说法static静态变量静态局部变量始终存在着,生存期为整个源程序;若未赋以初值,则由系统自动赋以0值。静态全局变量的作用域局限于一个源文件内C语言根据函数是否能被其它源文件调用,将函数分为内部函数与外部函数。如果函数只能被本源文件的函数调用,则称此函数为内部函数。在定义内部函数时,给函数定义前面加上关键字“static”。有了内部函数的概念后,在不同的源文件中可以有相同的函数名而不会发生冲突。7.8内部函数和外部函数7.8内部函数和外部函数例:staticintmax(inta,intb){}//max函数只能在该源文件中使用如果函数不仅能被本源文件的函数调用,还需要被其它源文件中的函数调用,则称此函数为外部函数。在定义内部函数时,给函数定义前面加上关键字“extern”。7.8内部函数和外部函数(续)例:externintmax(inta,intb){}//max函数可以在本工程文件中的所有源文件中使用注意两点:(1)如果在源文件A中调用另一个源文件B中的函数,那么必须在源文件A中对要调用的函数进行说明,格式如下:externintmax(inta,intb);(2)在C语言中由于函数本质上是外部的,C语言允许声明函数的时候缺省extern。7.9知识点强化与应用【例7-15】设计一个显示九九乘法口诀表的程序。#include<stdio.h>voidtable(){ inti,j;inta[9][9]; printf("\t\t\t九九乘法口诀表\n"); printf("-----------------------------------------------------------------------\n"); for(i=0;i<9;i++) { for(j=0;j<=i;j++) { a[i][j]=(i+1)*(j+1); printf("%d*%d=%d\t",(j+1),(i+1),a[i][j]);//利用二维数组输出公式 } printf("\n"); }}voidmain(){ table();//调用table函数,显示九九乘法表中的公式 printf("\n");}7.9知识点强化与应用(续)【例7-16】在例题7-15的基础增加一个查找的游戏功能,从键盘输入一个整数后,将九九乘法口诀表中的分解公式输出,如果口诀表中没有则显示“九九表中找不到能将此数分解的公式”。#include<stdio.h>voidtable(){ inti,j;inta[9][9]; printf("\t\t\t九九乘法口诀表\n"); printf("-----------------------------------------------------------------------\n"); for(i=0;i<9;i++) { for(j=0;j<=i;j++) { a[i][j]=(i+1)*(j+1); printf("%d*%d=%d\t",(j+1),(i+1),a[i][j]);//利用二维数组输出公式 } printf("\n"); }}7.9知识点强化与应用(续)voidresolve(intn){ inti,flag=0;//设置标志变量,在循环结束后判断是否 for(i=2;i<=8;i++) { if(n%i==0) { if(n/i<=9) { flag=1; printf("%d=%d*%d\n",n,n/i,i); } } } if(0==flag) printf("九九表中找不到能将此数分解的公式\n"); }7.9
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论