收藏 分销(赏)

用函数合理组织程序PPT课件.ppt

上传人:可**** 文档编号:777662 上传时间:2024-03-13 格式:PPT 页数:31 大小:194.50KB
下载 相关 举报
用函数合理组织程序PPT课件.ppt_第1页
第1页 / 共31页
用函数合理组织程序PPT课件.ppt_第2页
第2页 / 共31页
用函数合理组织程序PPT课件.ppt_第3页
第3页 / 共31页
用函数合理组织程序PPT课件.ppt_第4页
第4页 / 共31页
用函数合理组织程序PPT课件.ppt_第5页
第5页 / 共31页
点击查看更多>>
资源描述

1、6.1 模块化带来的好处从软件工程的角度上说,降低程序复杂性的有效方法是合理的模块化和局部化。在设计一个复杂的程序时,往往把整个程序划分为若干个功能较为单一的模块,分别予以实现,再把所有的模块像搭积木一样搭起来,这种在程序设计中分而治之的策略,被称为模块化程序设计方法。在C+中,这些模块就是一个个的函数,函数也是C+构造程序的重要的基本单位。语句是构成程序的最基本单位,函数也是由语句构成的,从程序设计的角度上说,函数(一系列语句)常被当成一个整体来看,这就降低了程序的复杂度。6.1.1 函数的调用过程在前面的学习中大家已经熟悉了main()函数,一个C+程序里包含一个主函数(即main函数)和

2、若干个其它函数,main()处于最顶层,其他函数作为其下层模块,main()函数调用其它函数,其它函数之间也可以互相调用。图6.1为一个C+程序的模块结构图,程序的执行过程为ABCDEFGHIJKMNABCDEFGHIJKMN。任何一个C+程序都是从main()函数开始执行,而且是只执行main()函数,从main()的前花括号开始,到main()的后花括号为止,在此过程中,如果碰到函数调用语句(如中的“函数1;”),便暂时中断main()的执行,将程序流程转到被调用函数(对应中的B B),执行完被调用函数或遇到return语句则返回main()函数(对应中的H H),继续执行,一个函数也可以

3、调用其他函数(如中函数1调用函数3),这时的调用过程与main()函数调用其他函数过程类似,常称为函数嵌套。6.1.2 抽象和封装面向过程的程序设计是基于功能分析的,最关心的是如何实现一个模块的功能以及如何使用这个模块,至于模块内部的结构,对其他模块来说是不重要的,完全可隐藏的。对函数而言,这个道理同样适用,在第2章主函数小节中提到了函数由函数头和函数体两部分组成,而函数头定义了函数和调用它的函数之间的接口。在C+程序中,函数可以看成一个封装体,将一系列相关的、实现某一功能的代码封装起来,并提供了一个使用方法(程序中常称接口),通过该接口可以在程序的任何地方使用这些代码完成特定功能,至于函数是

4、如何编写的,可能并不是用户关心的重点,用户真正关心的是这个函数如何使用。6.1.3 实现一个函数实现一个函数有3个步骤:定义、声明与调用,拿电影来做比喻,定义等价于电影的拍摄,声明等价于电影院得到放映许可,调用是电影院放电影,电影院可以自行拍摄,也可以拿别的单位拍的电影来放,在程序中,这意味着可以自己定义函数,也可以使用诸如标准库或第三方库提供的函数,但在使用前,都要进行声明,通知编译器函数的存在,以获得函数的使用许可,才能进行调用,声明后,程序可以多次调用函数,等同于电影院在获得放映许可后,可以多次放映影片。见示例代码61:6.2 函数定义函数定义由函数头和函数体两部分组成,其基本形式为:返

5、回类型 函数名(参数列表)函数体函数定义通过这一结构告诉编译器要进行的操作。最小的函数-无返回值、无参数void MyFunction1(void)6.2.1 函数头第一行“返回值类型 函数名(参数列表)”称为函数头,定义了函数和调用它的函数之间的接口:(1)函数名 上级函数通过函数名实现对函数的调用,函数名是一个符合C+语法要求的标识符,定义函数名与定义变量名的规则是一样的,但应尽量避免用下划线开头,因为编译器常常定义一些下划线开头的变量或函数。函数名应尽可能反映函数的功能,做到“望文知义”。(2)参数列表 0个或多个变量,用于向函数传送数值或从函数带回数值,每个参数都应采取“类型 变量名”

6、形式,参数列表中的参数称为形式参数,简称形参。编译器并不会在函数定义时为这些参数分配内存空间,只有在函数调用时,向函数传递了实参后,这些参数才称为程序实体,形参相当于剧本中的角色,而实参是演员,在中,函数定义中的x和y是剧本角色,而变量num1和num2是演员,num1扮演了x的角色,num2扮演了y的角色。如果参数表列中参数个数为0,我们称之为无参函数,无参函数可以定义为:返回类型 函数名()或或返回类型 函数名(void)(3)返回类型 指定函数用return返回的函数值的类型,如果函数没有返回值,返回类型应为void。C+对返回值的类型有一定限制,不能是数组,但可以是其他任何类型,如整型

7、、浮点型、指针,甚至是结构和共用体等。6.2.2 函数体花括号中的语句称为函数体,一个函数的功能,通过函数体中的语句来完成,函数体指明了函数要进行的操作及操作顺序。程序执行到函数体中的return语句返回,在函数体中可以有多个return语句,但函数只能有一个出口,换句话说,只执行一条返回语句,返回语句的基本形式为:return 表达式;表达式的类型应当与函数头中指定的返回类型一致,否则,编译器会根据函数头中指定的返回类型对表达式进行转换。返回主要起如下作用:(1)撤销函数调用时为参数和变量分配的栈内存空间;(2)向调用函数(上级)返回最多一个值(表达式的值);(3)将程序流程从当前函数返回上

8、级函数。代码6.1中,add函数的定义如下:int add(int x,int y)int z=x+y;return z;6.2.3 函数定义补充说明当函数的返回类型是void时,表明函数不向上级函数返回任何值,这时可以用一个空的“return;”语句,将程序流程返回,撤销函数调用时为参数和变量分配的栈内存空间,空的“return;”语句位于函数末尾时,该语句可以省略,用函数体的后花括号实现函数的返回即可。通常用返回类型为void的函数执行某些操作,见代码62。int main()void print();/函数声明print();/函数调用return 0;void print()/函数定义

9、,void表示没有返回值int n;cout请输入一个整数n:n;cout你输入的数是:nendl;6.3 函数声明函数声明,也称函数原型。函数声明,用以通知编译器函数的存在,以获得函数的使用许可,惟其如此,才能在程序中对函数进行调用。6.3.1 为什么要进行函数声明函数声明描述了函数和编译器间的接口,想要调用一个函数,必须在调用函数中必须对被调用函数进行说明。在代码61中,main()函数中的“int add(int x,int y);”用于在main()函数内声明add函数,使其在main()函数内可用,同时告诉编译器,add函数接收两个int型的输入参数,如果程序没有提供这样的参数,编译

10、器便会指出错误,或对传入的其他类型参数进行隐式转换,在add函数完成计算后,将把返回值放置到指定位置(可能是CPU寄存器,也可能是某个内存单元),然后上级函数(代码61中为main()函数)从这个位置取得返回值,add函数的声明指出了返回值类型为int,编译器借此知道应检索多少内存字节并对这些字节作出解释。图6.2形象化地说明了代码61中的函数声明的作用和返回值机制。6.3.2 如何声明一个函数函数声明类似于函数定义,不过没有实现代码,函数说明的一般形式如下:返回类型 函数名(参数表列);函数声明是一个语句,所以要以分号结束,在书写函数声明时,只要把函数头复制下来,并在末尾添加分号即可。语句结

11、尾处有无分号常常可用来区分是函数声明还是函数定义。如代码6.1中的“int add(int x,int y);”,不过,函数声明只要与函数定义一致,能提供给编译器足够的信息即可,因此,C+中的函数声明不要求提供变量名,add函数的声明可以写成:int add(int,int);从中编译器得知:函数名为add,接收两个int型的参数,返回值类型为int,这些信息已经足够,“int add(int x,int y);”中的变量名x和y仅仅起到增强程序可读性的作用,而且,这个变量名也可以与函数定义中的形参不同,也就是说,将声明语句写成下列形式丝毫不会影响程序的编译和运行。int add(int A,

12、int B);6.3.3 分割程序文件一些程序常常由许多文件组成,为了方便管理,常常将函数定义在cpp文件中,而将函数的声明语句放在与cpp文件同名的头文件(h文件)中,这样就可以通过编译预处理#include 或#include“xxx.h”实现函数的声明,这种方法在大型程序文件的组织中十分有用,见代码6.3。#include 用于C+系统提供的头文件,这些头文件一般位于C+系统目录下的include子目录下,而#include“xxx.h”适用于用户自己建立的头文件,预处理器接收到该指令后,会首先在当前文件所在目录中进行搜寻,如果找不到,再到C+系统头文件中寻找。6.4 函数调用在前面的代

13、码中,读者已经知道了如何调用一个函数,函数定义和函数声明的目的都是为了函数调用,唯有函数调用才是利用函数实现某个功能的过程,函数调用的基本形式为:函数名(实参列表);对于无参函数,其调用形式为:函数名();函数调用由函数名和函数调用运算符()组成,()内有0个或多个逗号分隔的参数(称为实参)。每一个参数是一个表达式,且参数的个数与参数的类型要与被调函数定义的参数(称为形参)个数和类型匹配,首先计算参数表达式的值,并将此值传递给形参。如果函数调用后有返回值,调用表达式可以用在表达式中,如中的 int numTotal=add(num1,num2);,而无参函数的调用必须是一个单独的语句,如 pr

14、int();。函数调用的主要作用是:(1)用实参向形参传递数据;(2)为获得数据的参数和函数中声明的变量分配临时存储空间;(3)中断当前正在运行的上级调用函数,将程序流程转到被调用函数的入口处。6.4.1 形参和实参形参和实参,类似与剧本角色和演员的关系,同一个角色可以由不同的演员来扮演,只有在演员扮演的过程中,角色才是鲜活、有意义的。前面的章节已经提到,在函数定义时,并不会为参数列表中的参数分配内存空间,只有在函数调用时,才为形参分配内存空间,并用实参的值为其赋值,在执行到函数结束时,程序会撤销调用过程中为参数和中间变量分配的内存空间。代码64演示了形参和实参的关系:int main()in

15、t n=2;cout 地址:&n endl;/输出实参变量n的地址cout 数值:n endl;void print(int);/函数声明print(n);/函数调用return 0;void print(int n)/函数定义cout 地址:&n endl;/输出形参n的地址cout 数值:n endl;程序运行结果:地址:0013FF7C数值:2地址:0013FF2C数值:2(注意:print函数中的参数n和main函数中的参数n占据不同的内存地址)6.4.2 参数类型转换调用函数时,如果实参类型与形参类型不匹配,编译器会对实参自动进行类型转换(隐式转换),形参的类型,取决于函数的声明语句

16、,见代码65。int main()double m=12;/声明一个double型变量mvoid print(int);/函数声明print(m);/函数调用时,实参m隐式转换为int型传递给形参return 0;void print(int n)/函数定义cout数值:nendl;程序运行结果:数值:26.4.3 值传递很多C+教科书在讲述函数传值调用时都会举下面这个例子,见代码66。int main()void change(int,int);/函数声明int x=2,y=3;cout 交换前:x=x ,y=y endl;change(x,y);/传值调用cout 交换后:x=x ,y=y

17、 endl;return 0;void change(int n,int m)/函数定义int temp;temp=n;n=m;m=temp;输出结果:交换前:x=2,y=3;交换后:x=2,y=3;6.4.4 指针传递要想被调函数改变调用函数中的变量值,应使函数中的操作直接作用在调用函数的变量上,要达到目的,一个有效途径是使用指针传递,先看代码67。int main()void change(int*,int*);/函数声明,形参为指针类型int x=2,y=3;cout 交换前:x=x ,y=y endl;change(&x,&y);/函数调用,传递的参数时两整形数的地址cout 交换后:

18、x=x ,y=y endl;return 0;void change(int*n,int*m)/函数定义int temp;temp=*n;*n=*m;*m=temp;输出结果:交换前:x=2,y=3;交换后:x=3,y=2;6.4.5 引用传递在第4章中已经提及,对变量的引用相当于变量的别名,以“int&m=n;”为例,对m的操作实质上就是对n的操作,例如湖南省的简称是湘,湘就可以是认为是湖南省的引用,新闻标题上说“湘发生”实质上等价于“湖南省发生”,所以说,m既不是n的拷贝,也不是指向n的指针,m就是n自己。C+允许函数通过引用实现参数传递,此时,在调用函数时,不会再为形参分配内存空间,因为

19、此时,形参就是实参自身,在函数中对形参进行的任何操作,本质上都是对调用函数中的实参进行操作,见代码68。int main()void change(int&,int&);/change函数声明,传递的是引用int x=2,y=3;cout变量x的地址是:&xendl;cout交换前:x=x,y=yendl;change(x,y);/change函数调用cout交换后:x=x,y=yendl;return 0;void change(int&n,int&m)/change函数定义int temp;temp=n;n=m;m=temp;cout形参n的地址是:&nendl;6.4.6 对3种传递的补

20、充(函数返回值)3种传递方式不仅仅是参数传递,函数返回某个值也有值传递、指针传递和引用传递3种方式。代码6.9是使用范例。返回值为指针和引用类型时,要注意函数内定义或声明的变量为自动(临时)变量,这些变量在函数结束后会被自动撤销,其占用的内存也会被系统收回;也就是说此时返回的指针和引用都是无效的。例如:int*minus(int m,int n)int k=0;int*z=&k;*z=m n;return z;函数执行完毕后,指针z和函数中的变量被撤销,可能导致程序崩溃。6.4.7 缺省参数调用缺省参数调用的基本思想是在声明语句中预先初始化一些参数的值,在调用语句中相应的参数可以缺省。缺省参数

21、调用的规则(1)定义时,缺省参数必须按照从右向左顺序;(2)调用时,如某一参数缺省,其后的参数也必须缺省。6.4.8 inline函数函数的引入可以减少程序的代码量,使得函数程序可以在代码间共享。但函数调用需要一些时空开销,在调用函数时,要中断调用函数,将执行流程转移到被调函数中,待被调函数执行完毕后,返回调用函数。因此,在调用函数前,应保护被调函数现场,记录程序当前位置,以方便从被调函数中返回,恢复现场,并从原来的位置处继续执行。对于较长的函数,这种开销可以忽略不计,但对于一些很短的、频繁调用的函数体,这种开销就不能不能忽视。举例来说,代码6.3中的print函数定义如下:void prin

22、t(int x)coutx0而言:n!=n*(n-1)!直观上,可以用循环语句计算n的阶乘,如下列代码所示:int Calc(int n)int res=1;for(int i=1;i=n;i+)res*=I;return res;6.6 函数的重载自然语言中,一个词可以有不同的含义,即该词被重载了,人们可以根据上下文判断该词的意义,在C+程序,可以将语义、功能相似的几个函数用同一个名字来表示,这样便于记忆,提高了函数的易用性,称为函数重载。函数重载的示例见代码613。6.6.1 何时使用函数重载将不相关的函数都用同样的名字来命名不是个明智的选择,将降低程序的可读性,使程序难以理解,因此,函数

23、重载必须用在合适的场合。函数名相同,最重要的一点是函数要完成的任务一样(至少是类似),比如,都用来输出信息,或都用来和某个硬件打交道等,避免函数名字相同,但功能完全不同的情形。其次,形式参数的类型应不同,对于形参类型相同,只有形参个数不同的场合,就无需定义两个函数,采用前面提及的缺省参数调用机制,只要定义一个函数即可。不过,如果形参类型不同,缺省参数调用便不再适用,此时,应使用函数重载。6.6.2 如何实现函数重载当调用多个同名的重载函数时,要求能够唯一地确定应执行哪一个函数,这是通过函数参数的个数和类型来区分的。所以,重载的函数要求参数个数或者参数类型上不同,否则会出现编译错误。C+中不允许

24、几个函数名相同、形参个数和类型也相同,仅仅是返回值不同的情形,否则,程序编译时会出现函数重复定义的错误,此时,编译器无法根据返回值不同决定具体要调用的函数。6.6.3 陷阱:隐式转换导致重载函数出现二义性调用重载的函数时,如果实参类型与形参类型不匹配,编译器会对实参自动进行类型转换。如果转换后仍然不能匹配到重载的函数,则会产生一个编译错误,见代码614。6.7 C+如何使用内存C+有3种管理数据内存的方式:自动存储(栈存储)、静态存储和动态存储(堆存储),不同的方式下,内存的分配形式,存在时间的长短都不同,其中,动态存储方式已经在第4章中进行了介绍,下面对自动存储和静态存储进行说明。6.7.1

25、 自动存储(栈存储)对于在函数的形参、内部声明的变量及结构变量等,编译器将在函数执行时为形参自动分配存储空间,在执行到变量和结构变量等的声明语句时为其自动分配存储空间,因此称其为自动变量(automatic variable),有的教科书也称其为局部变量,在函数执行完毕返回时,这些变量将被撤销,对应的内存空间将被释放。事实上,自动变量的生存期只局限于它所在的代码块。所谓代码块,是包含在花括号对中的一段代码,函数只是代码块的一种,比较下面两段代码:6.7.2 静态存储(编译器预分配)每个C+程序对应着一块静态数据区(也称全局数据区)。在源程序编译时,编译器就为某些程序实体(某些变量及所有的常量)

26、预分配存储地址和内存空间,程序一开始执行,这些程序实体就被创建,一直到程序结束才被撤销,并释放对应的内存空间,因此称其为“永久存储”。静态存储的程序实体的数目在程序运行过程中是不变的,因此无须使用特殊的机制(如堆、栈)来管理它们,编译器将分配固定的内存块来存储所有的静态存储实体。常量已经在第2章进行了详细的介绍,下面对静态存储的变量进行讨论,根据用法不同,静态存储的变量可分为“全局变量”和“静态变量”。6.8 作用域与可见域在函数一节,讨论形参变量时曾经提到:形参变量只在被调用期间才分配内存单元,调用结束则立即释放。这表明形参变量只存活在函数内,只有在函数内才有效,只有在函数内才能使用该变量。

27、程序流程离开该函数后,该变量便不复存在、不再有效、不能再使用了。这3条分别对应着变量的生存期、作用域和可见域。通俗地讲:生存期指的是在程序运行过程中,变量从创建到撤销的一段时间。生存期的长短取决于前面所讲的存储方式,对于自动分配(栈分配),变量与其所在的代码块共存亡;对于静态分配(编译器预分配),变量与程序共存亡,程序开始执行时即已存在,一致到程序运行完毕退出后才撤销;对于动态存储的内存块(注意:不是指向该内存块的指针),由程序员决定其生存期。在程序代码中,变量有效的范围(源程序区域)称为作用域,能对变量、标识符进行合法的访问的范围(源代码区域)称为可见域,变量有效的前提是变量存在于内存中,实

28、际上,变量的作用域和生存期一样,都取决于存储方式,可见域是作用域的子集。6.8.1 作用域严格地说,作用域是程序正文代码(不包括注释等)中的一片区域,在这块区域内,标识符理论上(排除下面要介绍的屏蔽情况)指向同一内存单元,可以将C+作用域分为以下几类:(1)块作用域:自动变量(auto、register)和内部静态变量(static)具有块作用域,在一个块内声明的变量,其作用域从声明点开始,到该块结束为止。函数定义中声明的形参,其作用域限定在该函数体内,与其他函数中声明的同名变量不是一回事,允许在不同的函数中使用相同的变量名,编译器将为这些变量分配不同的存储单元,不会混淆。(2)文件作用域:外部静态变量(static)具有文件作用域,从声明点开始到文件末尾,此处所指的文件是编译基本单位cpp文件。(3)全局(程序)作用域:全局变量(extern)具有全局作用域,只要在使用前对其进行声明,便可在程序(由若干个文件组成)的任意位置使用全局变量。(4)类作用域:这将在介绍了类的概念后详细介绍。

展开阅读全文
相似文档                                   自信AI助手自信AI助手
猜你喜欢                                   自信AI导航自信AI导航
搜索标签

当前位置:首页 > 包罗万象 > 大杂烩

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        获赠5币

©2010-2024 宁波自信网络信息技术有限公司  版权所有

客服电话:4008-655-100  投诉/维权电话:4009-655-100

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :gzh.png    weibo.png    LOFTER.png 

客服