c语言学习,c语言课件第11章.ppt

上传人:本田雅阁 文档编号:2142764 上传时间:2019-02-21 格式:PPT 页数:44 大小:132.51KB
返回 下载 相关 举报
c语言学习,c语言课件第11章.ppt_第1页
第1页 / 共44页
c语言学习,c语言课件第11章.ppt_第2页
第2页 / 共44页
c语言学习,c语言课件第11章.ppt_第3页
第3页 / 共44页
亲,该文档总共44页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述

《c语言学习,c语言课件第11章.ppt》由会员分享,可在线阅读,更多相关《c语言学习,c语言课件第11章.ppt(44页珍藏版)》请在三一文库上搜索。

1、第11章 C语言的模块化程序设计,Return,本章学习目标 掌握模块化程序设计的基本方法。 掌握全局变量和局部变量、动态变量和静态变量的概念和使用方法。 掌握内部函数和外部函数的概念和使用方法。 掌握由多个源文件组成的C程序设计和编译运行方法。,11.1 程序设计的基本方法 11.2 C语言对模块化编程的支持 11.3 多个源程序文件组成的C程序设计案例分析,11.1 程序设计的基本方法,结构化程序设计方法是随着结构化程序设计语言(如PASCAL、C)的出现和发展而建立起来的,已经被广泛应用于软件系统的开发。 结构化程序设计方法主要包括如下3个方面:1)自顶向下、逐步求精的设计过程。2)模块

2、化程序设计。3)结构化编程。,11.1.1 自顶向下、逐步求精的设计过程 所谓自顶向下、逐步求精的设计过程就是在进行一个复杂系统的设计时采取先全局后局部、先整体后细节、先抽象后具体的分析设计方法,把系统分解为层次分明、结构清晰、容易实现的若干个模块,然后再将每一个模块细化为若干个处理步骤或算法,直到可用程序设计语言的语句来编程实现。,下面用一个简单例子说明这种自顶向下、逐步求精的设计过程。,【例11-1】 设计一个简单的通讯录管理程序。,【思路导航】首先将问题分解成以下7个模块: (1)主控模块 (2)菜单模块 (3)添加模块 (4)删除模块 (5)修改模块 (6)显示模块 (7) 保存模块,

3、然后,对这7个模块再分别进一步细化。,主控模块可细化为: (1)初始化; (2)循环等待用户选择菜单项; (3)根据用户选择菜单项执行相应操作: 若是“添加”,则调用添加模块; 若是“删除”。则调用删除模块; 若是“修改”,则调用修改模块; 若是“显示”,则调用显示模块; 若是“保存”,则调用保存模块; 若是“退出”,则结束程序。,添加模块可细化为: (1)查找 (2)根据查找结果做出相应操作: 若该名字已存在,输出提示信息;若该名字未存在,插入新记录。 删除模块可细化为: (1)查找 (2)根据查找结果做出相应操作: 若该名字不存在,输出提示信息;若该名字存在,删除该记录。,修改模块可细化为

4、: (1)查找 (2)根据查找结果做出相应操作: 若该名字不存在,输出提示信息;若该名字存在,修改该记录。 保存模块可细化为: (1)提示输入文件名; (2)以写方式打开该文件; (3)把数据写入该文件,最后,把各个子模块再细化为具体的C语言语句。请看书中/*exam11_1*/。程序编译运行后的用户界面如图11-1所示。,演示,图11-1,11.1.2 模块化程序设计,1什么是程序模块? 在软件设计过程中,往往将一个大规模的程序划分成若干个大小适当的程序段去编写,或者是将那些重复使用的程序段进行独立设计,以达到计算机可以重复执行,而设计人员又不必重复去编写的目的。这样划分的程序段被称为程序模

5、块。,2什么是模块化程序设计? 模块化程序设计就是遵循一定的模块分解和组织原则,把一个大程序分解为多个容易理解和实现的大小适当、功能明确、具有一定独立性的程序模块的过程。,(1)模块分解原则 在进行模块分解时,要求各模块功能尽可能专一,各模块之间的联系尽可能简单。模块之间的联系越简单,独立性就越强,就越容易独立地进行设计、维护和修改,程序的可维护性和可扩展性就越好,程序设计的效率和质量也就越高。,(2)模块组织原则 结构化程序设计方法要求按层次结构组织各模块。“自顶向下”地将一个大程序逐层分解,得到程序的模块层次结构,而后再进一步把每个模块分解为具体的执行模块或执行步骤。 按层次组织模块时,一

6、般较上层的模块描述“做什么”,最底层的模块才描述“如何做”。,3C语言的模块化技术,(1)C语言的模块化粒度 在C语言中,函数是实现程序模块化的有力工具。一般地,一个函数实现一个模块功能,一个模块(或一个源程序文件)可以由多个函数组成。C语言的模块粒度从小到大依次为:函数源程序文件程序,如图11-1所示。,图11-1,(2)C语言编译系统对模块化的技术支持 C语言编译系统提供了宏定义、文件包含、条件编译等编译预处理命令来实现编译预处理,并且提供了丰富的库函数,为实现模块化编程提供了极大的方便。,(3)C语言对模块化的技术支持 C语言提供函数和局部变量以实现模块的包装和独立性(在第7章已介绍),

7、提供全局变量以实现多个模块之间的数据共享,提供extern声明的全局变量和外部函数以扩展其作用域从而提高模块之间的函数和数据的共享性,提供static声明的全局变量和内部函数以限制其作用域从而提高模块的相对独立性。本章11.2节将重点讨论这些技术的使用。,11.1.3 结构化编程,结构化编程是把任何程序的结构都限制为顺序、选择和循环三种基本结构,以提高程序的可读性和可靠性。 一个结构化程序应当具有以下特点: (1) 有一个入口、一个出口; (2) 没有死语句(永远执行不到的语句), 每一个语句应当至少有一条从入口到出口的路径通过它; (3) 没有死循环(无限制的循环)。 在本书前面的章节已经详

8、细介绍了顺序结构、选择结构和循环结构的程序设计,这里就不再重复。,11.2 C语言对模块化编程的支持,11.2.1 局部变量与全局变量 1. 定义变量的位置 C语言中定义变量的位置基本上有三种:在函数体内、函数体外和函数形式参数。在函数体内定义的变量以及函数的形式参数都称作局部变量,在所有函数体外定义的变量称作全局变量。 2. 局部变量 局部变量只在定义它们的函数内有效,在此函数之外不能使用它们。局部变量还可以在函数内的复合语句中定义,此时局部变量只在定义它们的复合语句中有效。常常把起于左大括号“”,止于右大括号“”内的语句块统称为代码块,所以可以说,局部变量仅在定义它们的代码块中有效。 例如

9、:,fun1() int z; /*z在函数fun1内有效*/ z=10; ,fun2(int x,int y) int z; /*x,y,z在函数fun2内有效*/ z=x+y; return z; ,fun3() int flag; /*flag在函数fun3内有效*/ scan(“%d”,flag); if(flag) char s30; /*s仅在if语句块中有效*/ printf(“please enter information:”); gets(s); ,注意:在C语言中,代码块中的局部变量必须在代码块开始处定义,即变量的定义必须出现在其它非变量定义语句之前。而在c+中则不存在此

10、限制,可以在任意地方定义变量。,例如: fun() int i;i=10; int j;j=20; j变量的定义出现在给i的赋值语句之后,在多数C编译系统中会报告语法错误。应改为: fun() int i;int j; i=10;j=20; ,3. 全局变量 与局部变量不同,全局变量可以自定义位置起被本源文件中的其他所有函数使用,也可以用extern声明后,在由多个文件组成的程序中被其它源文件使用(关于extern的作用在下一节关于变量存储类型中再详细讨论)。,【例11-2】全局变量的作用范围。 代码如下:,/*exam11_2*/ #include int count; /*定义全局变量co

11、unt*/ fun1() int temp; /*定义局部变量temp*/ temp=count; /*引用全局变量count*/ fun2(); printf(“ncount is %d, temp is %d ”,count,temp); int temp; /*定义全局变量temp*/ fun2() int count; /*局部变量count屏蔽了全局变量count*/ for(count=1;count=temp;count+)/*引用全局变量temp*/ putchar(*); int main(void) temp=10; /*对全局变量temp赋值*/ count=100; /

12、*对全局变量count赋值*/ fun1(); return 0; ,全局变量 count的作用范围,全局变量temp的作用范围,演示,虽然main函数和fun1函数都没有定义count,但由于在两个函数之前定义了全局变量count,所以main和fun1都可以使用count;main和fun2函数都没有定义temp,但由于在两个函数之前定义了全局变量temp,所以main和fun2函数都可以使用temp;但是temp是在fun1之后才定义的全局变量,所以如果在fun1函数中企图引用全局变量temp,则会报告出错。 fun2函数定义了与全局变量同名的局部变量count,所以全局变量count在

13、fun2中被屏蔽,即不起作用,而只使用局部变量count。fun1函数定义了与全局变量同名的局部变量temp,全局变量temp在本函数无效,并且就算fun1函数内没有定义同名的局部变量temp,也不能引用全局变量temp,因为全局变量temp在fun1函数之后才定义(若使用extern声明后扩展了有效范围则可以引用,参考下节关于变量的存储类型讨论)。,说明: (1)使用全局变量的好处 全局变量可以起到在不同函数间进行数据传递的作用,尤其当需要从一个函数返回多于一个的返回值时可以利用全局变量来实现。这是因为在一个函数中改变了全局变量的值,则会影响到所有使用该全局变量的函数。 (2)使用全局变量的

14、坏处 使用全局变量会使函数的通用性变。C语言的一大特点就是支持模块化编程,通过函数和局部变量实现对代码数据的包装,函数之间除了通过参数传递数据外尽量不要有其它任何依赖关系,使得程序的移植性好,可读性强。使用全局变量则使函数之间的依赖变强,降低程序的通用性。,在程序中大量使用全局变量容易引起程序错误,因为各个函数执行时都可能改变全局变量的值,容易产生副作用。 全局变量在程序的整个执行期间占用存储单元,而不像局部变量只在需要时占用存储空间。 鉴于上述原因,建议非不得已时不使用全局变量。,【例11-3】通过全局变量从函数得到多于一个的返回值。 编写一个函数,能够求出一组整数中的最大值和最小值。 【思

15、路导航】因为通过函数的return 语句只能返回一个值,所以把另一个结果赋给一个全局变量,从而达到返回两个结果的目的。,/*exam11_3*/ int Max=0; /*定义全局变量*/ int minimum(int array,int n) int min,j; Max=min=array0; for(j=0;jarrayj) min=arrayj; else if(Maxarrayj) Max=arrayj; return min; int main(void) int a10,k; for(k=0;k10;k+) scanf(“%d”,a+k); printf(“Max=%dnmin

16、=%dn”,Max, minimum(a,10); return 0; ,演示,1 2 3 4 5 6 7 8 9 10 Max=10 min=1,运行情况,注意:上面主函数中的printf函数的参数的前后位置。若把Max放在minimum(a,10)的右边,则得不到正确结果,因为C函数的参数运算顺序是从右到左,所以应把minimum(a,10)放在Max的右边,这样才能先调用minimum函数从而同时得到最大值和最小值的结果。,11.2.2 变量的存储类型,在C语言中,每一个变量有两个属性:数据类型和数据的存储类型。数据类型是指数据被定义为整形、字符型、实数型等,前面已详细介绍过。存储类型是

17、指数据在内存中的存储方法,分为两大类:静态存储和动态存储。 所谓静态存储是指在程序运行期间分配固定存储空间的方式;动态存储是指根据需要进行存储空间分配,不需要时即释放存储空间的方式。,变量的存储类型具体又分为四种类型,四种类型说明符如下: auto(自动) register(寄存器) static(静态) extern(外部),一般以如下形式对变量进行声明: 存储类型说明符 数据类型 变量名; 如: auto int a,b; static int c=1;,1. auto变量,函数的局部变量,如果不作static存储类型声明,都是动态分配存储空间的;函数的形式参数也是动态分配存储空间的,两者

18、都是在函数调用时系统给它们分配存储空间,在函数结束时系统自动释放这些存储空间。所以这类局部变量和形式参数都属于auto(自动)变量。,这类局部变量可用auto关键字声明,例如: int fun(int m) auto int j,k; /*定义j,k为自动变量,auto可以省略不写*/ 关键字auto可以省略不写,系统把没有进行static声明的局部变量和形式参数默认为自动型变量。,2. 用extern 声明全局变量,全局变量是在所有函数的外部定义的,它的作用范围是从定义位置起到本文件结束处,全局变量是静态分配存储空间的,使用extern可以扩展全局变量的作用范围。,为了解决不同文件可以使用同

19、一个全局变量的问题,常在一个文件中声明所有的全局变量,在其他文件中使用extern声明来自本文件之外的全局变量。如图11-2所示。,文件2中利用extern声明x和y为来自其它文件的全局变量,编译程序在编译时不再为这些变量分配存储空间,但是链接时会自动从外部模块中引用这些变量。 用extern声明外部变量时,类型名可以不写。 在同一个文件中,当全局变量在使用它的函数之后定义时,也可以在使用它的函数中利用extern对该变量做全局变量声明,如文件1中的全局变量z。,3. static变量,用static声明的变量为静态变量,其存储空间是系统在编译时静态分配的,生存期为整个程序的执行期。stati

20、c对局部变量和全局变量所起作用不同。下面分别介绍 (1) static局部变量 使用static声明局部变量时,编译系统为其分配固定存储单元,函数结束后变量所占空间不会被系统自动释放,也就是该变量的值可以被该函数再次使用。这对于某些需要在函数调用之间保持局部变量值的函数来说是非常重要的。,【例11-4】数据系列产生器。,下面函数f_series()每次产生一个新数,这个新数比上次值增加3,使用这个函数可以产生一个差为3、第一个数为10的等差数列。,/*exam11_4*/ #include f_series() static int number=10; number+=; return(nu

21、mber); int main(void) int k; for(k=0;k10;k+) printf(“%d ”,f_series()-3); return 0; ,演示,10 13 16 19 22 25 28 31 34 37,运行结果,首次进入函数代码时给局部变量number赋初值10,以后再次调用该函数时不再赋初值,而且number在整个程序运行期间一直存在,每次调用函数f_series()时新生成的数number都是在前次调用结束时的number值加上3。,注意:在函数调用之间如果需要某些数值保持可用,可以使用全局变量。但由于全局变量对所有函数开放,容易引起副作用。所以,如果需要保

22、留函数上一次调用结束时变量的值,最好是使用static局部变量,因为虽然static局部变量在函数调用结束后依然存在,但是只有本函数可以使用,其它函数不可使用,从而避免副作用。,(2) static全局变量,在全局变量前加上static存储类型说明符,限定了该变量只在本文件中有效,只有本文件的所有函数可以使用它。其它文件的函数无法使用它,修改它,从而可以消除副作用。,【例11-5】改写例11-4。,【思路导航】例11-4中函数f_series()所产生的系列只能是从10开始,如果我们需要产生从任意指定数开始的数据系列,则需要使用一个全局变量number和一个给number赋初值的函数。为了满足

23、这个要求,改写如下: 首先用指定起始值调用f_setstart(),初始化数据系列发生器,然后再连续调用f_series()产生等差数据系列。 代码如下:,/*exam11_5*/ #include static int number; /*定义全局变量number*/ f_setstart(int start) /*指定number的起始值*/ number=start; f_series() number+=3; return number; (续),int main(void) int k,start; printf(“input the start:”); scanf(“%d”, ,演

24、示,input the start:5 the series are: 5 8 11 14 17 20 23 26 29 32,运行结果,想一想:为什么主函数中要使用start=start-3;这条语句?,4. register变量,一般情况下,变量的值存放在内存单元中。但是有一些使用频繁的变量,比如循环控制变量和循环体内反复引用的局部变量,如果循环的次数很多,对这些变量存取时要消耗很多的时间,为了提高程序的执行效率,C语言允许将局部变量的值存放在CPU的寄存器中,这样可以直接从寄存器取出这些变量,从而大大提高存取的速度。,只有局部自动变量和形式参数可以声明为register变量,对全局变量和

25、静态局部变量不能声明为register类型,也就是说寄存器变量是动态存储类型。,注意:因为register变量可以保存在寄存器中,所以对这类变量不能使用取址操作符&取它的地址。另外,实际上大多数系统register只对整型和字符型变量起作用。当今优化的编译系统能够自动识别使用频繁的变量,从而将这些变量存放在寄存器中,并不需要编程者指定。,11.2.3 内部函数与外部函数,1. 函数的存储分类,函数也可以像变量一样,通过static和extern声明改变它的作用范围。只能被本文件的其它函数调用的函数被称为内部函数,能被其它文件中的函数调用的函数称为外部函数。,(1) 内部函数,声明内部函数时,在

26、函数名和函数类型的前面加static。 static 类型标识符 函数名(形参列表),例如: static int fun(int m,int n),内部函数又叫静态函数,和静态全局变量一样,由于在它们的前面使用static声明,使得其它文件不能引用它们,从而使得在不同的文件可以使用相同的文件名而不会产生干扰。这也是C语言支持程序模块化设计的一个特点。,【例11-7】内部函数举例。,/*exam11_7*/ /*file1.c*/ static int fun(int x,int y) /*声明函数fun为内部函数*/ int z; z=x+y; return z; int main(void

27、) extern fun1(int,int); int m,n,s; m=10; n=20; printf(“%d+%d=%dn”,m,n,fun(m,n); printf(“%d*%d=%dn”,m,n,fun1(m,n); ,/*file2.c*/ static int fun(int x,int y)/*声明函数fun为内部函数*/ int z; z=x*y; return z; int fun1(int m,int n) return fun(m,n); ,该程序由两个文件file1.c和file2.c组成,file1.c中的函数fun和file2.c中的函数fun同名,但是fun函数

28、在两个文件中的功能不同,由于在各自文件中作了static声明,所以互不干扰。file2.c中的函数fun1没有作static声明,所以可以被file1.c中的main函数调用。,演示,10+20=30 10*20=200,运行结果:,注意要使用Change dir修改为file1和file2所在目录,同时输入“Project name”为exam11_7.prj。,(2) 外部函数,声明外部函数时,在函数名和函数类型的前面加extern。,extern 类型标识符 函数名(形参列表),例如: extern int fun(int m,int n),外部函数可以被其它文件的函数调用。C语言规定,

29、如果在定义函数时省略extern,则隐含为外部函数。 虽然在定义外部函数时可以省略extern,但是在调用其它文件的函数时要用extern声明所用函数为外部函数或者利用函数原型进行声明。,【例11-8】外部函数举例。,利用外部函数实现:输入一个字符串,然后把字符串中所有数字字符删掉。,/*exam11_8*/ /*file1.c*/ void input(char s ) /*定义外部函数input*/ gets(s); /*输入字符串s*/ void delete(char s ) /*定义外部函数delete*/ int j,k; for(j=k=0;sj)!=0;j+) if(!(sj=

30、0 /*存放字符串结束符*/ (续),/*file2.c*/ int main(void) extern input(char s ); extern delete(char s ); char s80; input(s); delete(s); printf(”%s”,s); return 0; ,/*声明在本函数中将要调用的在其他文件中定义的函数input()和delete()*/,演示,abcd1276efgh7i4k90lmn65opq8 abcdefghiklmnopq,运行结果:,注意要使用Change dir修改为file1和file2所在目录,同时输入“Project name

31、”为exam11_8.prj。,11.3 多个源程序文件组成的C程序设计,在进行模块化程序设计过程中,应遵循以下几个原则: 1.每个功能模块必须只有单一入口和单一出口; 2.尽可能使用形式参数向函数传递信息,而不使用全局变量; 3.若有一些函数必须使用全局变量时,最好把这些全局变量和相关函数放入单独的一个文件中,并且将全局变量声明为static; 4.每个函数必须能够为调用者报告自己操作的状态,即报告调用是否成功等。,另外,在大型程序设计中,往往一个程序包含的函数个数非常庞大,要查找到所需函数非常不容易,所以有必要将所有函数进行分类存放。一般将在概念或功能上相关的函数组织成一个文件(源文件,也

32、叫一个模块)。例如,在一个通讯录管理程序中,可以将所有关于信息输入的函数存放在一个文件中;将所有关于信息查找的函数放在一个文件中;将所有关于删除的函数放在一个文件中,等等。,下面举一个简单的例子说明如何进行多个源程序组成的C程序设计以及编译运行。,【例11-9】下面是法国数学家加思帕提出来的一个趣味问题。,有15个基督徒和15个非基督徒乘坐的船在海上遇险,必须将一半的人投入海中,其余的人才能逃生。有人提出一个办法,让30个人围成一圈,从第一个人开始报数,数到第9的那个人就被投入海中,如此下去,直到剩下15人为止。结果15个基督徒全部幸运地留了下来。请问:当初30个人是如何排列的?,【思路导航】

33、可以使用一个具有30个数据元素的数组模拟30个人围成的圈,数组元素值为1时,表示该人留在船上,值为0时表示被投入海中。使用一个整型变量i记录被投入海中人数,使用一个整形变量k 记录当前报的数,使用一个整形变量j记录当前的数组元素下标。将数组的30个元素值都初始化为1。当k等于9时,将该数组元素值修改为0。,/*exam11_9*/ /*file1.c*/ #include void throw(int a30,int n); /*声明throw函数原型,这样*/ int people30; /*定义全局数组people*/ /*file2.c*/ void throw(int a30,int

34、n)/*指定报数为n即被投入大海*/ int i,k,j=0; for(i=0;i15;i+) for(k=0;) k=k+aj; if(k=n) /*报数为n,则修改该元素值为0,并且转去进行下一轮报数*/ aj=0; j=(j+1)%30;/*下标移动到第29后下一个位置自动转到0*/ break; j=(j+1)%30; (续),/*file3.c*/ #include /*此处应写出file1.c的完整路径*/ #include int main(void) extern people30; int i; for(i=0;i30;i+) peoplei=1; /*初始化全局数组*/ throw(people,9); /*调用throw函数*/ printf(“n“); for(i=0;i30;i+) printf(“%d“,peoplei); ,演示,111100000110111010011000100110,运行结果:,注意书中所介绍的“方法一”和“方法二”两种编译连接方式的不同之处。,作业与实验 作业:习题十一? 上机操作:实验十一,Return,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 其他


经营许可证编号:宁ICP备18001539号-1