C语言程序设计课件 第6章 模块与函数_第1页
C语言程序设计课件 第6章 模块与函数_第2页
C语言程序设计课件 第6章 模块与函数_第3页
C语言程序设计课件 第6章 模块与函数_第4页
C语言程序设计课件 第6章 模块与函数_第5页
已阅读5页,还剩47页未读, 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

程序设计基础目

录6.1模块化程序设计与函数6.2函数的定义6.3函数的调用6.4函数的嵌套调用与递归调用6.5数组作为函数的参数6.6变量的作用域与生存期*6.7内部函数与外部函数6.1.1模块与函数在C语言中,模块可以用函数来实现。简单来说,函数就是功能,每个函数用来实现一个特定的功能,可以通过函数名来标识不用的功能模块。函数编写好后,使用时可以只关心函数的功能和使用方法而不必关心函数功能的具体实现。然后按照一定的规则调用这些函数,就组成了解决某个特定问题的程序。这样有利于代码重用,可以提高软件开发效率、增强程序的可靠性,也便于分工合作和修改维护。因此,模块化的程序设计是通过设计函数和调用函数来实现的。6.1.1模块与函数【例6-1】输入三角形的边长,求三角形的周长和面积。解题分析:这个问题可以分解成3部分,输入、求周长、求面积。(1)输入3条边长的功能可以调用scanf()函数完成。scanf("%f,%f,%f",&a,&b,&c);(2)计算周长,并输出。floatperi(floata1,floatb1,floatc1){floatp;//定义周长pp=a1+b1+c1;returnp;}(3)计算面积,并输出。floatarea(floata1,floatb1,floatc1,floatp1){floatAre;//定义面积AreAre=sqrt(p1*(p1-a1)*(p1-b1)*(p1-c1));returnAre;}6.1.1模块与函数【例6-1】输入三角形的边长,求三角形的周长和面积。/*ch06-01.c*/#include<stdio.h>#include<math.h>intmain(){floata,b,c;floatPerim,Area;printf("输入3条边的边长:");scanf("%f,%f,%f",&a,&b,&c);Perim=peri(a,b,c);//计算周长Area=area(a,b,c,Perim/2);//计算面积printf("周长为:%f,面积为:%f",Perim,Area);return0;}6.1.1模块与函数模块设计应遵循如下原则:模块独立,容易测试和维护。模块之间的关系简单,模块实现标准化,可以进行局部替换和修改,不会造成整个程序的混乱。大小适中。模块功能复杂影响可读性,模块太小增加程序的复杂度。根据不用任务要求具体确定函数大小。层次化分解。采用“自顶向下”的结构化程序设计思路,逐步细化求精。6.1.2函数概述C语言的程序由一个或者多个函数构成。每一个完整的C语言程序有且仅有一个main函数,程序的执行总是从main函数开始,如果在main函数中调用其他函数,在调用后流程返回到main函数,并最终在main函数中结束整个程序的运行。所有函数是平行的,也就是说,在定义函数时是分别进行的,是相互独立的。一个函数并不从属于另一个函数,即函数不能嵌套定义。函数之间可以相互调用,调用其他函数的函数称为主调函数,被调用的函数称为被调函数。一个函数可以调用其他函数,也可以被另外的函数调用,但是不能调用main函数。main函数是被操作系统调用的。6.1.2函数概述从用户使用函数的角度来看,函数分为两种:标准库函数和用户自定义函数。标准库函数。如前面我们已经使用过print()、scanf()等函数都是标准库函数,C语言强大的功能完全依赖于它有丰富的库函数。库函数是由系统提供的,用户不必自己定义,可以直接使用,使用这些库函数时,只要在程序的开头用编译预处理命令把该函数所在的头文件包含进来即可。例如,为了调用printf()、scanf()等标准输入输出函数,需要在程序开头用#include<stdio.h>包含stdio.h头文件;为了调用sqrt()、sin()等数学函数,需要在程序开头用#include<math.h>包含math.h头文件。用户自定义函数。使用者根据自己都需要编写以解决专门需求的函数,更改更加灵活地满足用户的个性化需求。6.2函数的定义函数定义的一般形式为:类型名函数名(形式参数列表) //函数首部{ //循环体

声明部分

语句部分}6.2函数的定义【例6-2a】定义求两个整数中较大值的max函数。intmax(inta,intb){intc;c=a>b?a:b; //求a和b中的较大值returnc;}说明:函数定义包括“函数首部”和“函数体”两部分。其中,函数首部给出了该函数的返回类型、函数名、每个参数的次序和类型。由大括号“{}”括起点部分称为函数体,给出函数功能的具体实现。函数名的命名规则与标识符一样,尽量采用有意义的名字,方便程序阅读。形式参数列表给出用逗号分隔的多个形式参数,简称为形参。每个形参需要给出参数类型和参数名。形参列表的一般形式:类型1形参1,类型2,形参2,……说明:函数体放在一对大括号“{}”中,主要由两部分组成:声明部分:用来定义函数中用到的局部变量、声明内部要调用的函数。语句部分:也是函数的主体部分,是函数的具体功能实现,可以由顺序、选择、循环、函数调用等语句构成。函数体可以为空,称为空函数。调用此函数时,不做任何操作,也就没有任何实际作用。当然,也可以先用一个空函数站一个位置,等以后扩充程序功能时再补充。这样,程序的结构清晰,可读性好,方便扩充新功能。所以空函数在程序设计中往往是有用的。函数可以有一个返回值,也就是需要返回给主调函数的处理结果。6.3.1函数调用的形式1.函数调用的一般形式函数调用的一般形式为:

函数名(实际参数列表);2.函数调用方式根据被调函数在主调函数中的出现的位置和完成的功能不同,可以有以下3种函数调用方式:(1)函数调用语句。

scanf("%f,%f,%f",&a,&b,&c);(2)函数表达式。 m=max(x,y)/2;(3)函数参数。

m=max(max(x,y),z);6.3.1函数调用的形式【例6-2b】输入2个数,调用【例6-2a】max函数的main函数。/*ch06-02.c*/#include<stdio.h>intmain(){intx,y,z;scanf("%d%d",&x,&y);z=max(x,y); //调用max函数printf("%d",z);return0;}6.3.2函数声明在一个函数中调用另一个函数时,需具备以下条件:被调函数必须是已经定义的函数。如果被调函数是库函数,应在本程序开头用预处理命令#include将包含此库函数的头文件“包含”到本程序中。例如:#include<stdio.h> //使用输入输出库的函数如果被调函数是用户自定义的函数,并且该函数定义的位置在主调函数调用它之前,那么在主调函数中直接调用该函数即可。而若该函数定义的位置在主调函数调用它之后,则在函数调用前必须对该被调函数进行函数声明(declaration)。函数声明的一般形式为:类型名函数名(形参列表);6.3.2函数声明例如,如果【例6-2a】和【例6-2b】合并时没将max函数的定义放在main函数前面,则需要在main函数中进行max函数的函数声明.#include<stdio.h>intmain(){intmax(inta,intb);

//对max函数做声明intx,y,z;scanf("%d%d",&x,&y);z=max(x,y);printf("%d",z);return0;}6.3.3参数传递1.形参和实参在调用有参函数时,主调函数和被调函数之间通过参数进行数据传递。根据前面的叙述,在定义函数时的参数称为“形式参数”或“虚拟参数”,而调用函数时的参数称为“实际参数”,分别简称为“形参”和“实参”。2.函数的参数传递值传递。实参可以是常量、变量或表达式,形参为变量。函数被调用时,系统给形参分配内存空间,并用实参的值按从右到左的顺序复来初始化形参。这个过程是参数值的单向传递过程,一旦形参获得了值便于实参脱离关系,此后形参的任何改变都不会影响到实参。这种参数传递的方式称为值传递。6.3.3参数传递【例6-3】分析下列程序,理解参数的传递。/*ch06-03.c*/#include<stdio.h>voidswap(inta,intb)//交换两个变量{intc=a;printf("a=%d,b=%d\n",a,b);a=b;b=c;printf("a=%d,b=%d\n",a,b);}intmain(){intx=6,y=9;printf("x=%d,y=%d\n",x,y);swap(x,y);//调用swap函数printf("x=%d,y=%d\n",x,y);return0;}6.3.3参数传递【例6-3】分析下列程序,理解参数的传递。执行主函数中的函数调用:swap(x,y)swap函数中:c=a6c66xa99yb66xa99yb69xa99yb69xa96yba=bb=c6c6c返回主函数后:6x9y6.3.3参数传递地址传递。3.函数的返回值函数的返回值通常是通过函数中的return语句来获得,如【例6-2a】的max函数中有“returnc;”,则把变量c的值返回到主调函数中。Return语句的一般形式为:return表达式;或者:return(表达式);6.3.3参数传递【例6-4】微改【例6-2】,输入两个浮点数,求两个数中较大值并输出(max函数的函数类型定义为int型)。/*ch06-04.c*/#include<stdio.h>intmax(floata,floatb)//定义max函数{floatc;c=a>b?a:b;returnc;}intmain(){floatx,y;intz;scanf("%f%f",&x,&y);z=max(x,y);//调用max函数printf("%d",z);return0;}6.4.1函数的嵌套调用C语言中的函数定义是相互平行的、独立的,函数与函数之间没有从属关系,也就是说,在一个函数的定义中不能在定义另一个函数,即不能嵌套定义,但允许在函数的定义中调用另一个函数,而该函数自身会被其他函数调用,从而形成了函数的嵌套调用。6.4.1函数的嵌套调用【例6-5】求两个整数最小公倍数。/*ch06-05.c*/#include<stdio.h>intfgcd(inta,intb)//求最大公约数{intr;r=a%b;while(r!=0){a=b;b=r;r=a%b;}returnb;}intflcm(inta,intb)//求最小公倍数{intnum,r;r=fgcd(a,b);num=a*b/r;returnnum;}intmain(){intx,y,temp;intLcmu;printf("请输入两个正整数:");scanf("%d%d",&x,&y);if(x<y){temp=x;x=y;y=temp;}Lcmu=flcm(x,y);//调用函数求最小公倍数printf("最小公倍数为:%d",Lcmu);return0;}main()调用flcm()结束flcm()调用fgcd()返回fgcd()返回①②③④⑤⑥⑦⑧⑨6.4.2函数的递归调用一个函数在执行过程中调用了它自身,称为函数的递归调用。C语言允许递归调用。在函数体内直接调用本函数,称为直接递归。若某函数调用其它函数,而其它函数又调用了该函数,则称为间接调用直接递归(b)间接递归f()调用f()f1()调用f2()f2()调用f1()

图6-3函数的递归调用6.4.2函数的递归调用【例6-6】用递归法计算n!。

/*ch06-06.c*/#include<stdio.h>longfac(intn)//递归求n!{if(n<0)printf("n<0,dataerror.");//n不能小于0if(n==0||n==1)return1; //n=0,1时,n!=1elsereturn(n*fac(n-1)); //n>1时,n!=n*(n-1)!}intmain(){intn;printf("inputanintergernumber:");scanf("%d",&n);printf("%d!=%d\n",n,fac(n));//调用fac函数return0;}6.4.2函数的递归调用【例6-7】用递归法计算斐波那契数列的第n项(n>0)。

/*ch06-07.c*/#include<stdio.h>intfibonacci(intn)//递归求斐波那契数列{if(n<=0)printf("n<0,dataerror.");//n必须大于0if(n==1||n==2)return1;//递归终止条件elsereturn(fibonacci(n-1)+fibonacci(n-2));//递归关系}intmain(){intn;printf("inputanintergernumber:");scanf("%d",&n);printf("%d\n",fibonacci(n));//求斐波那契数列的第n项return0;}6.5数组作为函数的参数调用有参函数时,需要提供实参,数组也可以作为函数的参数使用,进行数据传递。数组作为函数参数有以下两种情况:一种是把数组元素作为实参使用,另一种是把数组名作为函数的形参和实参使用。6.5.1用数组元素作函数实参数组元素相当于带下标的普通变量,所以也可以作为函数的参数。但是数组元素只能作为函数的实参,不能用作形参,因为形参是在函数被调用时临时分配存储单元的,不可能为一个数组元素单独分配存储单元(数组是一个整体)。将数组元素作为实参的用法与变量相同,就是将数组元素的值传递给形参,实现从实参到形参的单向值传递。6.5.1用数组元素作函数实参【例6-8】定义函数,求一个整数数组中的最大值。/*ch06-08.c*/#include<stdio.h>intmax(inta,intb)//求较大值函数{intc;c=a>b?a:b;returnc;}intmain(){inta[10],i,Max;printf("input10intergernumber:\n");for(i=0;i<10;i++)scanf("%d",&a[i]);//输入10个数到数组中Max=a[0];//将当前最大值赋初值为a[0]for(i=1;i<10;i++)//将Max依次与数组中的其他元素比较Max=max(Max,a[i]);//调用max函数,数组元素作为实参printf("Themaxnumberis%d\n",Max);return0;}6.5.2用数组名作函数参数除了可以用数组元素作为函数参数外,还可以用数组名作函数参数,包括实参和形参。若用数组名作为函数的实参,则形参也应使用数组名(或指针变量,见第7章)。数组名代表数组首元素的地址,并不代表数组中的全部元素,因此,用数组名作函数实参时,不是把实参数组的值传递给形参,而是把实参数组首元素的地址传递给形参。这种参数传递称为地址传递。从而实参数组和形参数组共用同一段内存空间,在函数中对形参数组的访问就是访问实参数组。具体原理整理不做详细介绍,请参看第7章指针。6.5.2用数组名作函数参数【例6-9】修改【例6-8】定义用数组名作参数的函数,求一个整数数组中的最大值。/*ch06-09.c*/#include<stdio.h>intmain(){intmax(intb[10]);//函数声明inta[10],i;printf("input10intergernumber:\n");for(i=0;i<10;i++)scanf("%d",&a[i]);//输入10个数到数组中printf("Themaxnumberis%d\n",max(a));//调用max函数,用数组名作实参return0;}intmax(intb[10])//求最大值函数,用数组作形参{intm,i;m=b[0];for(i=1;i<10;i++)//将m依次与数组中的其他元素比较m=m>b[i]?m:b[i];returnm;}6.5.2用数组名作函数参数【例6-10】一维数组中存放了某门课程的成绩,编程求各门课程的平均分。/*ch06-10.c*/#include<stdio.h>intmain(){floataverage(floata[],intn);//函数声明floatmath[12]={76,84,92,70,65,90,56,77,61,85,91,80};floatenglish[10]={86,80,72,64,75,89,65,70,55,90};printf("averagemathis%6.2f\n",average(math,12));//数学平均分printf("averageenglishis%6.2f\n",average(english,10));//英语平均分return0;}floataverage(floata[],intn)//求平均分{floatAver,Sum=0;inti;for(i=0;i<n;i++)//用Sum累加成绩Sum+=a[i];Aver=Sum/n;//用Aver存放平均分returnAver;}6.5.2用数组名作函数参数关于数组名做函数参数的几点说明:(1)实参数组和形参数组的类型要一致,如不一致,结果将出错。(2)实参数组通过“地址传递”将实参数组首元素的地址传递给形参数组名,实际上形参和实参为同一数组,因此,形参数组发生变化时,实参数组也就被变化。利用这一特点,可以将函数处理中得到的多个结果值带回到主调函数中。(3)实参数组和形参数组的大小可以不一致。因为实际上形参数组并不存在,C语言编译系统也不检查形参数组大小,如此可以省略形参数组的大小,但要保留方括号“[]”,如【例6-10】中的“floataverage(floata[],intn)”。6.5.2用数组名作函数参数【例6-11】二维数组中存放了4位同学两门课程的成绩,编程求每位同学的平均分。/*ch06-11.c*/#include<stdio.h>intmain(){voidaverage(floats[][2],floata[]);//函数声明voidoutput(floata[]);//函数声明floatscore[4][2]={{76,86},{84,80},{92,72},{70,64}};floatave[4];inti,j;average(score,ave);//调用函数求平均分printf("averagescore:\n");output(ave);return0;}voidaverage(floats[][2],floata[])//定义函数求每位同学平均分{floatSum;inti,j;for(i=0;i<4;i++){Sum=0;for(j=0;j<2;j++)Sum+=s[i][j];//用Sum累加成绩a[i]=Sum/2;//用a[i]存放平均分}}voidoutput(floata[]) //定义输出函数{inti;for(i=0;i<4;i++)printf("%5.3f\n",a[i]);}6.6.1变量的作用域变量的作用域就是变量能够其作用的有效范围。前面讨论函数的形参时已得知,函数的形参只有在函数被调用时才分配内存空间,调用结束后立即释放。这表明形参变量只有在本函数内才有效,离开该函数就不能再使用了。C语言中的变量,按作用域范围的不同看分为两种:局部变量和全局变量。1.局部变量在函数内定义的变量,称为局部变量(也称为内部变量)。局部变量只有在定它的函数内有效,也就是说,其作用域仅限于该函数内从定义该变量开始到该函数结束为止,离开这些范围后就不能使用这些变量。函数的形参也是函数的局部变量,其作用域为整个函数内部。1.局部变量在函数内定义的变量,称为局部变量(也称为内部变量)。局部变量只有在定它的函数内有效,也就是说,其作用域仅限于该函数内从定义该变量开始到该函数结束为止,离开这些范围后就不能使用这些变量。函数的形参也是函数的局部变量,其作用域为整个函数内部。6.6.1变量的作用域6.6.1变量的作用域关于局部变量的作用域的几点说明:main函数。中定义的变量也是局部变量,也只能在主函数内部使用,不能在其他函数中使用。主函数同样不能使用其它函数定义的变量。形参是被调函数的局部变量,而实参是主调函数的局部变量。在不同函数内可以定义同名的变量,它们代表不同的对象,相互不影响。复合语句内也可以定义自己的局部变量,其作用域在在复合语句范围内。编译时,编译系统不为局部变量分配内存空间,而是在程序的运行过程中,当局部变量所在函数被调用时,编译系统根据需要临时分配内存空间,调用结束时,空间被释放。6.6.1变量的作用域2.全局变量在函数外部定义的变量,称为全局变量,也称为外部变量。它不属于哪个函数,而属于它所在的源程序文件。其作用域是从定义处开始到整个源程序文件结束。6.6.1变量的作用域【例6-12】分析下列程序,理解同名变量的处理。6.6.1变量的作用域【例6-13】采用全局变量求【例6-1】:输入三角形的边长,求三角形的周长和面积。//ch06-13.c*/#include<stdio.h>#include<math.h>floatPerim;floatarea(floata1,floatb1,floatc1){floatAre,p;Perim=a1+b1+c1;p=Perim/2;Are=sqrt(p*(p-a1)*(p-b1)*(p-c1));returnAre;}intmain(){floata,b,c;floatArea;printf("输入3条边的边长:");scanf("%f,%f,%f",&a,&b,&c);Area=area(a,b,c);//计算面积printf("周长为:%f,面积为:%f",Perim,Area);return0;}6.6.2变量的生存期和存储类型变量的存储方式由变量的存储类型决定。C语言提供了四种存储类型:自动类型auto、静态类型static、寄存器类型register和外部类型extern。1.自动变量(auto)auto类型是C语言程序中使用最广泛的一种类型,在函数中未加存储类型声明的变量均视为自动变量,即,auto类型是局部变量的默认类型,属于动态存储方式。自动变量用关键字auto作为存储类型的声明。格式如下:[auto]数据类型变量名;6.6.2变量的生存期和存储类型2.寄存器变量(register)一般情况下,变量(包括静态存储方式和动态存储方式)的值都是存放在内存中的,它们都是由编译程序在内存中分配存储单元。当程序要用到哪个变量时,由控制器发出指令将内存中该变量的值送到运算器中,运算结束后如果需要存储,再从运算器将数据送到内存中存储。如果有些变量要频繁使用,为了提高效率,C语言允许将变量的值存放在CPU的寄存器中,需要时直接从寄存器读写而不需要访问内存。由于对寄存器的访问速度远高于对内存的访问速度,所以这样做可以提高效率。存放在寄存器中的变量称为寄存器变量,用关键字register做定义,其一般形式为:register数据类型变量名;6.6.2变量的生存期和存储类型3.外部变量(extern)用extern声明的变量,称为外部变量,extern只能用来声明全局变量。在C语言源程序文件中,全局变量的作用域是从变量的定义处开始到本源程序文件结束,该文件中的各函数都可以访问这个变量。但是,可以通过extern声明语句将扩展其作用域。其声明的一般形式为:extern[数据类型]全局变量名;6.6.2变量的生存期和存储类型(1)在一个文件内扩展外部变量的作用域intf1(){ inta,b; externfloatk; //声明k为外部变量

……}floatk;//定义全局变量kintmain(){inti,j;……}外部变量k有效作用域扩展到从声明处开始6.6.2变量的生存期和存储类型(2)将外部变量的作用域扩展到其他文件一个C语言源程序可以由一个或多个源程序文件构成。如果需要在同一个源程序的多个源文件中访问同一个外部变量,那么可以在一个源文件中定义这个全局变量,而在其他的源程序文件中使用extern关键字将这个变量作“外部变量声明”。这样,在编译和连接时,编译系统会由此指导这个变量有“外部链接”,可以从别的源文件中找到这个变量的定义,并将该变量的作用域扩展到本文件中。:6.6.2变量的生存期和存储类型【例6-14】用两个源程序文件编程求a*b和ab的值。ch06-14a.c文件:#include<stdio.h>intA,B; //定义全局变量intmain(){intpower();//声明函数

printf("输入a和b:");scanf("%d,%d",&A,&B);printf("%d*%d=%d\n",A,B,A*B);//计算A*Bprintf("%d**%d=%d\n",A,B,power());//调用函数计算A**Breturn0;}ch06-14b.c文件:#include<stdio.h>externA,B; //声明外部变量,作用域从ch06-14a.c文件扩展到本文件intpower()//定义计算A**B函数{inti,Pow=1;for(i=1;i<=B;i++)Pow=Pow*A;returnPow;}6.6.2变量的生存期和存储类型4.静态变量(static)自动变量属于动态存储方式,函数调用结束后,其值不再保留。而有时希望函数中的局部变量的值在函数调用结束后仍然保留其值,则可以定

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论