版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、函数与预编译第1页,共92页,2022年,5月20日,9点24分,星期一模块化思想模块各司其职每个模块只负责一件事情,它可以更专心便于进行单个模块的设计、开发、调试、测试和维护等工作一个模块一个模块地完成,最后再将它们集成开发人员各司其职按模块分配任务,职责明确并行开发,缩短开发时间分而治之(Wirth, 1971 )信息隐藏(Parnas, 1972)第2页,共92页,2022年,5月20日,9点24分,星期一 在结构化程序设计中,函数是将任务进行模块划分的基本单位。一个函数实现一项功能。 在面向对象程序设计中,函数是对数据的一项操作,也是实现一项功能。第四章 函数与预编译 要掌握函数的使用
2、,必须理解函数调用时的内部实现机制,以及与此相关的内存分配机制、变量生命期和作用域。 本章还将介绍关于函数重载的概念,介绍递归算法、内联函数、默认参数函数以及多文件组织、编译预处理、工程文件的概念和运行库函数。第3页,共92页,2022年,5月20日,9点24分,星期一第四章 函数与预编译 4.1 函数的定义与调用 4. 5 作用域与标识符的可见性 4.4 函数调用机制 4.3 全局变量和局部变量 4.2 函数的参数传递,返回值及函数声明 4.10 编译预处理 4.9 头文件与多文件结构 4.6 存储类型与标识符的生命期 4.8 函数的一些高级议题 4.7 函数的递归调用 第4页,共92页,2
3、022年,5月20日,9点24分,星期一 4.1 函数的定义与调用 函数概述 函数的定义 函数的调用 第5页,共92页,2022年,5月20日,9点24分,星期一 函数概述函数是C+程序的基本组成模块。通过函数,可以把一个复杂任务分解成为若干个易于解决的小任务。充分体现逐步细化的设计思想。组成C+程序的若干函数中,有一个称为main()函数,是程序执行的入口,它可以调用其他函数,但不可以被调用。而其他一般函数既可以调用也可以被调用。函数概念的引入:入口函数:第6页,共92页,2022年,5月20日,9点24分,星期一 函数概述main ( )fun2( )fun1( )fun3( )funa(
4、 )funb( )func( )图4.1 函数调用层次关系第7页,共92页,2022年,5月20日,9点24分,星期一 函数概述4.1.1结束库函数和自定义函数: 库函数或标准函数,是由编译系统预定义的,如一些常用的数学计算函数、字符串处理函数、图形处理函数、标准输入输出函数等。 库函数都按功能分类,集中说明在不同的头文件中。用户只需在自己的程序中包含某个头文件,就可直接使用该文件中定义的函数。 用户根据需要将某个具有相对独立功能的程序定义为函数,称自定义函数。第8页,共92页,2022年,5月20日,9点24分,星期一4.1.2 函数的定义无参函数定义格式为:数据类型函数名(void)函数体
5、说明: 数据类型指函数返回值类型,可以是任一种数据类型,默认为返回整型值(但新标准要求写明,不用默认方式)。没有返回值应将返回值类型定义为void。 函数名采用合法标识符表示。 对无参函数,参数括号中的void通常省略,但括号不能省略。 函数体由一系列语句组成。函数体可以为空,称为空函数。 1 无参函数第9页,共92页,2022年,5月20日,9点24分,星期一4.1.2 函数的定义例: 打印一个表头void TableHead ( ) cout*endl;cout* example *endl;cout*=b?a:b); 第11页,共92页,2022年,5月20日,9点24分,星期一问题:定
6、义函数时究竟哪些变量应当作为函数的参数?哪些应当定义在函数体内?提示原则:函数在使用时被看成 “黑匣子”,除了输入输出外,其他部分可不必关心。从函数的定义看出,函数头正是用来反映函数的功能和使用接口,它所定义的是“做什么”。即明确了“黑匣子”的输入输出部分,输出就是函数的返回值,输入就是参数。因此,只有那些功能上起自变量作用的变量才必须作为参数定义在参数表中;函数体中具体描述“如何做”,因此除参数之外的为实现算法所需用的变量应当定义在函数体内。 C+中不允许函数的嵌套定义,即在一个函数中定义另一个函数。第12页,共92页,2022年,5月20日,9点24分,星期一4.1.3 函数的调用函数调用
7、: 所谓函数调用,就是使程序转去执行函数体。 在C+中,除了主函数外,其他任何函数都不能单独作为程序运行。任何函数功能的实现都是通过被主函数直接或间接调用进行的。 无参函数的调用格式: 函数名( ) 有参函数的调用格式: 函数名(实际参数表)其中实际参数简称实参,用来将实际参数的值传递给形参,因此可以是常量、具有值的变量或表达式。【例4.1】 输入两个实数,输出其中较大的数第13页,共92页,2022年,5月20日,9点24分,星期一4.2 函数的参数传递、返回值及函数声明 4.21 函数的参数传递及传值调用 4.23 函数声明4.22 函数返回值 第14页,共92页,2022年,5月20日,
8、9点24分,星期一参数传递: 函数调用首先要进行参数传递,参数传递的方向是由实参传递给形参。 传递过程是,先计算实参表达式的值,再将该值传递给对应的形参变量。一般情况下,实参和形参的个数和排列顺序应一一对应,并且对应参数应类型匹配(赋值兼容),即实参的类型可以转化为形参类型。而对应参数的参数名则不要求相同。 函数的参数传递及传值调用 传值调用和引用调用:按照参数形式的不同,C+有两种调用方式:传值调用和引用调用。传值调用传递的是实参的值,本章介绍传值调用。第15页,共92页,2022年,5月20日,9点24分,星期一 函数的参数传递及传值调用 传值调用:将实参的值复制给形参,在函数中参加运算的
9、是形参,而实参不会发生任何改变。传值调用起了一种隔离作用。【例4.2】 实参和形参对应关系的示例。第16页,共92页,2022年,5月20日,9点24分,星期一4.2.2 函数返回值return语句的格式:return 表达式;函数的计算结果通过该语句传递回主调函数。【例4.3】设计函数,根据三角形的三边长求面积。如果不能构成三角形,给出提示信息。分析:函数为计算三角形面积,一般三角形返回面积值,若不能构成三角形则返回-1。设计一个主函数完成函数测试。根据返回值情况输出相应结果。第17页,共92页,2022年,5月20日,9点24分,星期一4.2.2 函数返回值函数可以有返回值,也可以没有返回
10、值。对于没有返回值的函数,功能只是完成一定操作,应将返回值类型定义为void ,函数体内可以没有return语句,当需要在程序指定位置退出时,可以在该处放置一个: return ;讨论:4.2.2结束第18页,共92页,2022年,5月20日,9点24分,星期一 函数声明 函数声明是一条以分号结束的语句:函数返回值类型函数名 (形参表); 语法上对程序文件中函数的排列次序要求满足先定义后使用。但从结构化程序设计的角度,通常是先调用后定义。使用函数声明,则既符合由粗到精的思维方式,又满足了语法要求。 其中形参表可以逐个列出每个参数的类型和参数名,也可以列出每个形参的类型,参数名可省略,各形参之间
11、以逗号分隔。函数声明和所定义的函数必须在返回值类型、函数名、形参个数和类型及次序等方面完全对应一致,否则将导致编译错误。 函数声明的引入:函数声明的格式:第19页,共92页,2022年,5月20日,9点24分,星期一 下面是一个使用结构化程序设计思想开发的企业管理报表程序的框架。它使用了函数声明。void menu_print();void account_report();void engineering_report();void marketing_report();int main() int choice; do menu_print();cinchoice; while(choic
12、e=4); switch(choice) case 1: account_report(); break; case 2: engineering_report(); break; case 3: marketing_report(); break; return 0;第20页,共92页,2022年,5月20日,9点24分,星期一void menu_print() cout”系统功能:”endl; cout”1财务报表”endl; cout”2工程报表”endl; cout”3市场报表”endl; cout”选择业务序号:”; void account_report() /生成财务报表void
13、 engineering_report() /生成工程报表 void marketing_report() /生成市场报表;第21页,共92页,2022年,5月20日,9点24分,星期一 函数声明【例4.4】 输出所有满足下列条件的正整数m:10m1000且m、m2、m3均为回文数。分析:回文指左右对称的序列。如121、353等就是回文数。判断整数是否回文数用函数实现,其思想是将该数各位拆开后反向组成新的整数,如果该整数与原数相等则为回文数。m m*m m*m*m11 121 1331101 10201 1030301111 12321 1367631 运行结果:第22页,共92页,2022年
14、,5月20日,9点24分,星期一4.3 全局变量和局部变量4.4.1 变量的存储机制与C+的内存布局 4.4.2 全局变量 4.4.3 局部变量 第23页,共92页,2022年,5月20日,9点24分,星期一4.4.1 变量的存储机制与C+的内存布局自由存储区 (动态数据)栈区(函数局部数据)(main()函数局部数据)全局数据区(全局、静态)代码区(程序代码) 操作系统为一个C+程序的运行所分配的内存分为四个区域,如图4.3 所示:第24页,共92页,2022年,5月20日,9点24分,星期一存储区域说明:(1)代码区(Code area):存放程序代码,即程序中各个函数的代码块;(2)全局
15、数据区(Data area):存放全局数据和静态数据;分配该区时内存全部清零,结果变量的所有字节自动初始化为零。(3)栈区(Stack area):存放局部变量,如函数中的变量等;分配栈区时不处理内存,即变量取随机值。(4)自由存储区(Free store area):存放与指针相关的动态数据。分配自由存储区时不处理内存。参见第七章。4.4.1 变量的存储机制与C+的内存布局第25页,共92页,2022年,5月20日,9点24分,星期一 4.4.2 全局变量 在所有函数之外定义的变量称为全局变量。全局变量在编译时建立在全局数据区,在未给出初始化值时系统自动初始化为全0。全局变量可定义在程序开头
16、,也可定义在中间位置,该全局变量在定义处之后的任何位置都是可以访问的,称为可见的。【例4.5】 多个函数使用全局变量的例子。全局变量引入:第26页,共92页,2022年,5月20日,9点24分,星期一4.4.3 局部变量 定义在函数内或块内的变量称为局部变量。程序中使用的绝大多数变量都是局部变量。局部变量在程序运行到它所在的块时建立在栈中,该块执行完毕局部变量占有的空间即被释放。局部变量在定义时可加修饰词auto,但通常省略。局部变量在定义时若未初始化,其值为随机数。局部变量引入:【例4.6】 使用局部变量的例子。第27页,共92页,2022年,5月20日,9点24分,星期一4.4 函数调用机
17、制 局部变量占用的内存是在程序执行过程中“动态”地建立和释放的。这种“动态”是通过栈由系统自动管理进行的。(1)建立栈空间;(6)恢复现场:取主调函数运行状态及返回地址,释放栈空间;(7)继续主调函数后续语句。(5)释放被调函数中局部变量占用的栈空间;(4)执行被调函数函数体;(3)为被调函数中的局部变量分配空间,完成参数传递;(2)保护现场:主调函数运行状态和返回地址入栈;调用过程:第28页,共92页,2022年,5月20日,9点24分,星期一4.4 函数调用机制 void fun1(int, int);void fun2(float);int main() int x=1;y=2; fun
18、1(x, y); return o;void fun1(int a,int b) float x=3; fun2(x);void fun2(float y) int x; x栈顶栈底y3fun2()fun1()运行状态及返回地址x3b2a1fun1()main()运行状态及返回地址y2x1main()操作系统运行状态及返回地址此图例说明在程序执行过程中怎样通过栈“动态”地建立和释放局部变量占用的内存的第29页,共92页,2022年,5月20日,9点24分,星期一 4.5 作用域与标识符的可见性3 文件作用域 2 函数声明作用域 作用域:指标识符能够被使用的范围。只有在作用域内标识符才可以被访问
19、(称为可见)。本节重点讨论局部域和文件域(全局域),其中局部域包括块域和函数声明域。任何标识符作用域的起始点均为标识符说明处。下面分别介绍:1 块作用域 第30页,共92页,2022年,5月20日,9点24分,星期一 函数中定义的标识符,包括形参和函数体中定义的局部变量,作用域都在该函数内,也称作函数域。块域块指一对大括号括起来的程序段。块中定义的标识符,作用域在块内。复合语句是一个块。函数也是一个块。复合语句中定义的标识符,作用域仅在该复合语句中。【例4.7】 输入两数,按从大到小的顺序保存。块的引入:第31页,共92页,2022年,5月20日,9点24分,星期一块域由VC+平台运行,结果如
20、下:输入两整数:3 5调用前:实参a=3,b=5调用中交换前:形参a=3,b=5交换后:形参a=5,b=3调用后:实参a=3,b=5 交换失败局部变量具有局部作用域使得程序在不同块中可以使用同名变量。这些同名变量各自在自己的作用域中可见,在其它地方不可见。【例4.8】设计函数完成两数交换,用主函数进行测试。第32页,共92页,2022年,5月20日,9点24分,星期一块域 对于块中嵌套其它块的情况,如果嵌套块中有同名局部变量,服从局部优先原则,即在内层块中屏蔽外层块中的同名变量,换句话说,内层块中局部变量的作用域为内层块;外层块中局部变量的作用域为外层除去包含同名变量的内层块部分。如果块内定义
21、的局部变量与全局变量同名,块内仍然局部变量优先,但与块作用域不同的是,在块内可以通过域运算符“:”访问同名的全局变量。【例4.9】 显示同名变量可见性。第33页,共92页,2022年,5月20日,9点24分,星期一函数声明作用域 函数声明不是定义函数,在作函数声明时,其中的形参作用域只在声明中,即作用域结束于右括号。正是由于形参不能被程序的其他地方引用,所以通常只要声明形参个数和类型,形参名可省略。第34页,共92页,2022年,5月20日,9点24分,星期一3 文件作用域 文件作用域也称全局作用域。定义在所有函数之外的标识符,具有文件作用域,作用域为从定义处到整个源文件结束。文件中定义的全局
22、变量和函数都具有文件作用域。如果某个文件中说明了具有文件作用域的标识符,该文件又被另一个文件包含,则该标识符的作用域延伸到新的文件中。如cin和cout是在头文件iostream中说明的具有文件作用域的标识符,它们的作用域也延伸到嵌入iostream的文件中。第35页,共92页,2022年,5月20日,9点24分,星期一存储类型(storage class)决定标识符的存储区域,即编译系统在不同区域为不同存储类型的标识符分配空间。由于存储区域不同,标识符的生命期也不同。所谓生命期,指的是标识符从获得空间到空间释放之间的期间,标识符只有在生存期中、并且在其自己的作用域中才能被访问。4.6 存储类
23、型与标识符的生命期 4.6.1 存储类型 4.4.2 生命期 第36页,共92页,2022年,5月20日,9点24分,星期一自动变量为用auto说明的变量,通常auto缺省。局部变量都是自动变量,生命期开始于块的执行,结束于块的结束,其原因是自动变量的空间分配在栈中,块开始执行时系统自动分配空间,块执行结束时系统自动释放空间。故自动变量的生命期和作用域是一致的。4.6.1 存储类型 为提高程序运行效率,可以将某些变量保存在寄存器中,即用register说明为寄存器变量,但不提倡使用。C+中关于存储类型的说明符(storage class specifier)有四个:auto、register、
24、static和extern。其中用auto和register修饰的称为自动存储类型,用static修饰的称为静态存储类型,用extern修饰的称为外部存储类型。1 自动存储类型第37页,共92页,2022年,5月20日,9点24分,星期一static说明的变量称为静态变量。根据定义的位置不同,还分为局部静态变量和全局静态变量,也称内部静态变量和外部静态变量。静态变量均存储在全局数据区,如果程序未显式给出初始化值,系统自动初始化为全0,且初始化只进行一次;静态变量占有的空间要到整个程序执行结束才释放,故静态变量具有全局生命期。4.6.1 存储类型局部静态变量是定义在块中的静态变量,当块第一次被执
25、行时,编译系统在全局数据区为其开辟空间并保存数据,该空间一直到整个程序结束才释放。局部静态变量具有局部作用域,但却具有全局生命期。2 静态存储类型【例4.10】 自动变量与局部静态变量的区别第38页,共92页,2022年,5月20日,9点24分,星期一4.6.1 存储类型3 外部存储类型一个C+程序可以由多个源程序文件组成。多文件程序系统可以通过外部存储类型的变量和函数来共享某些数据和操作。在一个程序文件中定义的全局变量和函数缺省为外部的,即其作用域可以延伸到程序的其他文件中。其他文件如果要使用这个文件中定义的全局变量和函数,应该在使用前用“extern”作外部声明。外部声明通常放在文件的开头
26、(函数总是省略extern)。外部变量声明不同于全局变量定义,变量定义时编译器为其分配存储空间,而变量声明则表示该全局变量已在其他地方定义过,编译系统不再分配存储空间。外部的全局变量或函数加上static修饰,就成为静态全局变量或静态函数。静态的全局变量和函数作用域限制在本文件,其他文件即使使用外部声明也无法使用该全局变量或函数。 【例4.11】外部存储类型的例子第39页,共92页,2022年,5月20日,9点24分,星期一 生命期1. 静态生命期 静态生命期(Static extent或Static storage duration)指的是标识符从程序开始运行时就存在,具有存储空间,到程序运
27、行结束时消亡,释放存储空间。具有静态生命期的标识符存放在全局数据区,如全局变量、静态全局变量、静态局部变量。具有静态生命期的标识符在未被用户初始化的情况下,系统会自动将其初始化为0。 函数驻留在代码区,也具有静态生命期。所有具有文件作用域的标识符都具有静态生命期。第40页,共92页,2022年,5月20日,9点24分,星期一 生命期2. 局部生命期 在函数内部或块中定义的标识符具有局部生命期(Automatic extent或Automatic storage duration),其生命期开始于执行到该函数或块的标识符定义处,结束于该函数或块的结束处。具有局部生命期的标识符存放在栈区。具有局部
28、生命期的标识符如果未被初始化,其内容是随机的,不可引用。 具有局部生命期的标识符必定具有局部作用域;但反之不然,静态局部变量具有局部作用域,但却具有静态生命期。第41页,共92页,2022年,5月20日,9点24分,星期一 生命期具有动态生命期(dynamic extent或dynamic storage duration)的标识符存放在自由存储区,由特定的函数调用或运算来创建和释放,如用new运算符(或调用malloc()函数)为变量分配存储空间时,变量的生命期开始,而用delete运算符(或调用free()函数)释放空间或程序结束时,变量生命期结束。关于new运算符和delete运算符将在
29、第七章中介绍。4. 动态生命期第42页,共92页,2022年,5月20日,9点24分,星期一4.7 函数的递归调用 递归是一种描述问题的方法,或称算法。递归的思想可以简单地描述为“自己调用自己”。例如用如下方法定义阶乘:可以看出是用阶乘定义阶乘,这种自己定义自己的方法称为递归定义。递归的引入:第43页,共92页,2022年,5月20日,9点24分,星期一递归定义的阶乘函数:fac(int n)if (n=0|n=1) return 1;else return n*fac(n-1);只要设计主函数调用阶乘函数,即可实现计算阶乘。4.7 函数的递归调用【例4.12】 求4!运行结果:4321126
30、244!=24说明:cout”n4!=”fac(4)endl;执行时是先算函数值,然后再从左到右输出各表达式的值。所以有两行输出,而不是第一行插在第二行赋值号与24之间。第44页,共92页,2022年,5月20日,9点24分,星期一4.7 函数的递归调用探讨:计算是先右后左。请看下一条输出语句:cout”n4!=”fac(4) ”n3!=”fac(3)=0&ch=9?1:0;int main() char ch; while(cin.get(ch), ch!= n) if (IsNumber(ch) cout是数字字符 endl; else cout不是数字字符 endl; return 0;
31、因使用频度很高,说明为内联函数。第55页,共92页,2022年,5月20日,9点24分,星期一4.9 头文件与多文件结构 (选读)4.9.1 头文件标准库头文件:考虑标识符在其他文件中的可见性。使用头文件是很有效的方法。如:#includeusing namespace std; 其中的iostream是在标准名字空间域std中定义的头文件。对应的传统方式的文件名为,头文件以“.h”为后缀。 系统定义的头文件中定义了一些常用的公用标识符和函数,用户只要将头文件包含进自己的文件,就可使头文件中定义的标识符在用户文件中变得可见,也就可以直接使用头文件中定义的标识符和函数。 第56页,共92页,20
32、22年,5月20日,9点24分,星期一4.9.1 头文件自定义头文件:除了系统定义的头文件外,用户还可以自定义头文件。对于具有外部存储类型的标识符,可以在其他任何一个源程序文件中经声明后引用,因此用户完全可以将一些具有外部存储类型的标识符的声明放在一个头文件中。具体地说,头文件中可以包括:用户构造的数据类型(如枚举类型),外部变量,外部函数、常量和内联函数等具有一定通用性或常用的量,而一般性的变量和函数定义不宜放在头文件中。第57页,共92页,2022年,5月20日,9点24分,星期一4.9.2 多文件结构 在开发较大程序时,通常将其分解为多个源程序文件,每个较小的程序用一个源程序文件建立。程
33、序经过建立、编译、连接,成为一个完整的可执行程序。多文件结构通过工程进行管理,在工程中建立若干用户定义的头文件.h和源程序文件.cpp。头文件中定义用户自定义的数据类型,所有的程序实现则放在不同的源程序文件中。编译时每个源程序文件单独编译,如果源程序文件中有编译预处理指令,则首先经过编译预处理生成临时文件存放在内存,之后对临时文件进行编译生成目标文件.obj,编译后临时文件撤销。所有的目标文件经连接器连接最终生成一个完整的可执行文件.exe。 图4.11是一个多文件系统的开发过程。第58页,共92页,2022年,5月20日,9点24分,星期一4.9.2 多文件结构 编译 预编译编译 预编译 预
34、编译编译图4.11 C+程序开发过程file1.hfile1.cppfile2.hfile2.cppfilen.hfilen.cpp临时文件1临时文件2临时文件nfile1.objfile2.objfilen.objFilename.exe.libC+标准类库连接运行第59页,共92页,2022年,5月20日,9点24分,星期一4.10 编译预处理(选读) 4.10.1 宏定义指令 4.10.2 文件包含指令 4.10.3 条件编译指令 第60页,共92页,2022年,5月20日,9点24分,星期一4.10.1 宏定义指令1 不带参宏定义用来产生与一个字符串对应的常量字符串,格式为:#defi
35、ne 宏名 常量串 预处理后文件中凡出现该字符串处均用其对应的常量串代替。替换过程称为宏替换或宏展开。例如,如果使用指令#define PI 4.1415926则程序中可以使用标识符PI,编译预处理后产生一个中间文件,文件中所有PI被替换为4.1415926。宏替换只是字符串和标识符之间的简单替换,预处理本身不做任何数据类型和合法性检查,也不分配内存单元。第61页,共92页,2022年,5月20日,9点24分,星期一4.10.1 宏定义指令2 带参数的宏定义带参宏定义的形式很象定义一个函数,格式为:#define 宏名 ( 形参表 ) 表达式串 例如作如下宏定义:#define S(a,b)
36、(a)*(b)/2程序中可使用S(a,b),预处理后产生中间文件,其中S(a,b)被替换成(a)*(b)/2。注意,宏定义时形参通常要用括号括起来,否则容易导致逻辑错误。例如,如果定义:#define S(a,b) a*b/2那么程序中的S(3+5,4+2)就会被宏展开为3+5*4+2/2,不符合定义的真正的意图。带参宏定义形式上象定义函数,但它与函数的本质不同,宏定义仍然只是产生字符串替代,不存在分配内存和参数传递。 第62页,共92页,2022年,5月20日,9点24分,星期一4.10.2 文件包含指令文件包含用#include指令,预处理后将指令中指明的源程序文件嵌入到当前源程序文件的指
37、令位置处。格式为:#include 或#include 文件名第一种方式称为标准方式,预处理器将在include子目录下搜索由文件名所指明的文件。这种方式适用于嵌入C+提供的头文件,因为这些头文件一般都存在C+系统目录的include子目录下。而第二种方式编译器将首先在当前文件所在目录下搜索,如果找不到再按标准方式搜索。这种方式适用于嵌入用户自己建立的头文件。第63页,共92页,2022年,5月20日,9点24分,星期一一个被包含的头文件中还可以有#include指令,即include指令可以嵌套,但是,如果同一个头文件在同一个源程序文件中被重复包含,就会出现标识符重复定义的错误。例如:头文件
38、f2.h中包含了f1.h,如果文件f4.cpp中既包含f1.h,又包含f2.h,那么编译将提示错误,原因是f1.h被包含了两次,那么其中定义的标识符在f4.cpp中就被重复定义。避免重复包含可以用条件编译指令。4.10.2 文件包含指令第64页,共92页,2022年,5月20日,9点24分,星期一4.10.3 条件编译指令 1 用宏名作为编译的条件格式为:#ifdef#else#endif2 表达式的值作为编译条件格式为:#if #else#endif当希望在不同条件下编译程序的不同部分。这种情况就要使用条件编译指令。 其中程序段可以是程序也可以是编译预处理指令。可以通过在该指令前面安排宏定义
39、来控制编译不同的程序段。第65页,共92页,2022年,5月20日,9点24分,星期一例:在调试程序时常常要输出调试信息,而调试完后不需要输出这些信息,则可以把输出调试信息的语句用条件编译指令括起来。形式如下:#ifdef DEBUGcouta=atx=xendl;#endif在程序调试期间,在该条件编译指令前增加宏定义:#define DEBUG调试好后,删除DEBUG宏定义,将源程序重新编译一次。条件编译指令包括:#if、#else、#ifdef、#ifndef、#endif、#undef等。#ifndef与#ifdef作用一样,只是选择的条件相反。#undef指令用来取消#define指
40、令所定义的符号,这样可以根据需要打开和关闭符号。第66页,共92页,2022年,5月20日,9点24分,星期一第四章 函数再见谢谢!第67页,共92页,2022年,5月20日,9点24分,星期一4.1.3 函数的调用【例4.1】 main( )函数调用max(2.5,4.7 )函数max(2.5,4.7 )return 4.7 主程序后续语句【例4.1】 输入两个实数,输出其中较大的数。其中求两个实数中的较大数用函数完成。程序如下:#include using namespace std;float max(float a,float b)return(a=b?a:b);int main()f
41、loat x,y;cout输入两个实数:xy;coutx和y中较大数为max(x,y)endl;return 0;第68页,共92页,2022年,5月20日,9点24分,星期一 函数的参数传递及传值调用【例4.2】 调用power(4.6,3 )函数power(4.6,3 )return 97.336 主程序后续语句n= 3x= 4.6c= a【例4.2】 实参和形参对应关系的示例。float power(float x,int n)/求x的n次幂float p=1;while(n-) p*=x;return p; int main()int n=3;float x=4.6;char c=a;
42、coutpower(x,n)=power(x,n)endl;coutpower(c,n)=power(c,n)endl;coutpower(n,x)=power(n,x)endl;return 0;第69页,共92页,2022年,5月20日,9点24分,星期一 函数的参数传递及传值调用【例4.2】调用power(a,3 )函数power(97,3 )return 912673 主程序后续语句n= 3x= 4.6c= a【例4.2】 实参和形参对应关系。float power(float x,int n) /求x的n次幂float p=1;while(n-) p*=x;return p; int
43、 main()int n=3;float x=4.6;char c=a;coutpower(x,n)=power(x,n)endl;coutpower(c,n)=power(c,n)endl;coutpower(n,x)=power(n,x)endl;return 0;第70页,共92页,2022年,5月20日,9点24分,星期一 函数的参数传递及传值调用【例4.2】调用power(3,4.6 )函数power(3,4)return 81 主程序后续语句n= 3x=4.6c= a【例4.2】 实参和形参对应关系。float power(float x,int n) /求x的n次幂float p
44、=1;while(n-) p*=x;return p; int main()int n=3;float x=4.6;char c=a;coutpower (x,n)=power(x,n)endl;coutpower (c,n)=power(c,n)endl;coutpower (n,x)=power(n,x)endl;return 0;第71页,共92页,2022年,5月20日,9点24分,星期一【例4.3】程序:float TriangleArea(float a, float b, float c) if (a+b=c)|(a+c=b)|(b+c=a) return -1; float s
45、; s=(a+b+c)/2; return sqrt(s*(s-a)*(s-b)*(s-c) ;int main() float a,b,c,area; cout输入三角形三边a,b,c:abc; area=TriangleArea(a,b,c); if(area=-1) cout(a,b, c )不能构成三角形!endl; elsecout三角形(a,b,c )面积为:areaendl; return 0;第72页,共92页,2022年,5月20日,9点24分,星期一bool palindrome(int); /函数声明int main() int m;coutsetw(10)msetw(2
46、0)m*m“setw(20)m*m*mendl;for(m=11;m1000;m+)if(palindrome(m)&palindrome(m*m) &palindrome(m*m*m)coutsetw(10)msetw(20)m*msetw(20)m*m*0);for(intj=0;ji;j+) n=n*10+digitj;return (n=m);【例4.4】 输出回文数第74页,共92页,2022年,5月20日,9点24分,星期一4.4.2 全局变量【例4.5】 打印200调用func( )函数func( )200*2=400打印400n=100n=100*2=200【例4.5】 多个函
47、数使用全局变量的例子。int n=100;void func()n*=2; int main()n*=2;coutnendl;func();coutnendl; return 0;第75页,共92页,2022年,5月20日,9点24分,星期一4.4.3 局部变量 打印main()中的t=4.5调用fun( )函数fun( )打印fun()中的t=5 打印main()中的t=4.5t= 4.5t = 5【例4.6】 使用局部变量的例子。void fun()auto int t=5;/ fun()中的局部变量,auto可省略coutfun()中的t=tendl;int main()float t=
48、4.5;/main()函数中的局部变量coutmain()中的t=tendl;fun();coutmain()中的t=tendl;return 0;第76页,共92页,2022年,5月20日,9点24分,星期一块域【例4.7】 a= 3 b= 535a=3 b=5a=5 b=3【例4.7】 输入两数,按从大到小的顺序保存,并输出结果。结果栈t = 3 int main() int a,b; /具有函数域 cout输入两整数:ab; cout“a=atb=b=a)int t; /具有块域 t=a; a=b; b=t; /交换a,b的值 couta=atb=bendl; return 0;上述程序
49、若在最后一个cout语句处增加:couttendl;则编译时会提示错误,因为变量t的作用域只在if语句中,其它地方不可见。第77页,共92页,2022年,5月20日,9点24分,星期一【例4.8】设计函数完成两数交换,用主函数进行测试。void swap(int,int);int main() int a,b; /a,b作用域为main() cout输入两整数:ab; cout调用前:实参a=a, b=bendl; swap(a,b); /传值 cout调用后:实参a=a,b=bendl; return 0;void swap(int a,int b) /a,b作用域为swap() cout调
50、用中endl; cout交换前:形参a=“ a,b=bendl; int t; t=a; a=b; b=t;/交换swap()中的a,b的值 cout交换后:形参a=a,b=bendl;第78页,共92页,2022年,5月20日,9点24分,星期一全局n= 100 200 300内 i= 500内 j= 600内n=500+600 =11001100 500 600100200+300=500500 500 200 300外部 i=200外部 j=300【例4.9】 显示同名变量可见性。int n=100;#include int main() int i=200,j=300;cout nti
51、tjendl; /内部块 int i=500,j=600,n; n=i+j; cout ntitj endl; /输出局部变量n cout:nendl;/输出全局变量n n=i+j;/修改全局变量cout ntitj endl;return 0;第79页,共92页,2022年,5月20日,9点24分,星期一【例4.10】 自动变量与局部静态变量的区别 int st() static int t=100; /局部静态变量 t+; return t;int at() int t=100; /自动变量 t+;return t;int main() int i; for(i=0;i5;i+) cout
52、at()t; coutendl; for(i=0;i5;i+) coutst()t; coutendl; return 0;4.6.1 存储类型i= 0t= 10012345101101101101101第80页,共92页,2022年,5月20日,9点24分,星期一4.6.1 存储类型i= 0t=10012101345102103104105【例4.10】 自动变量与局部静态变量的区别 int st() static int t=100; /局部静态变量 t+; return t;int at() int t=100; /自动变量 t+;return t;int main() int i; f
53、or(i=0;i5;i+) coutat()=t; coutendl; for(i=0;i5;i+) coutst()t; coutendl; return 0;第81页,共92页,2022年,5月20日,9点24分,星期一【例4.11】外部存储类型的例子。假定程序包含两个源程序文件Ex4_11_1.cpp和Ex4_11_2.cpp,程序结构如下:/* Ex4_11_1.cpp ,由main()组成*/void fun2(); /外部函数声明,等价于extern void fun2();int n; /全局变量定义int main() n=1; fun2(); / fun2()定义在文件Ex4
54、_11_2.cpp中 coutn=nendl; return 0;/* Ex4_11_2.cpp,由fun2()组成*/extern int n; /外部变量声明,n定义在文件Ex4_11_1.cpp中void fun2() /fun2()被文件Ex4_11_1.cpp中的函数调用n=3; 运行结果:n=3第82页,共92页,2022年,5月20日,9点24分,星期一【例4.12】 求4!#include int fac(int n)int y; coutnt;if(n=0|n=1) y=1; else y=n*fac(n-1);coutyt;return y;int main()coutn4
55、!=fac(4)endl;return o; n=4cout4;y=4*fac(3);fac(4)=cout2;y=2*fac(1); n=2cout1;y=1;cout1;return 1; n=1 n=3cout3;y=3*fac(2);cout24;return 24;cout6;return 6;cout2;return 2;24第83页,共92页,2022年,5月20日,9点24分,星期一4.7 函数的递归调用【例4.13】 【例4.13】 汉诺塔问题。有A、B、C三根柱子,A柱上有n个大小不等的盘子,大盘在下,小盘在上。要求将所有盘子由A柱搬动到C柱上,每次只能搬动一个盘子,搬动过
56、程中可以借助任何一根柱子,但必须满足大盘在下,小盘在上。打印出搬动的步骤。 A柱B柱C柱第84页,共92页,2022年,5月20日,9点24分,星期一分析:1 A柱只有一个盘子的情况: A柱C柱;2 A柱有两个盘子的情况:小盘A柱B柱,大盘A柱C柱,小盘B柱C柱。3 A柱有n个盘子的情况:将此问题看成上面n-1个盘子和最下面第n个盘子的情况。n-1个盘子A柱B柱,第n个盘子A柱C柱,n-1个盘子B柱C柱。问题转化成搬动n-1个盘子的问题,同样,将n-1个盘子看成上面n-2个盘子和下面第n-1个盘子的情况,进一步转化为搬动n-2个盘子的问题,类推下去,一直到最后成为搬动一个盘子的问题。这是一个典型的递归问题,递归结束于只搬动一个盘子。4.7 函数的递归调用【例4.13】第85页,共92页,2022年,5月20日,9点24分,星期一4.7 函数的递归调用【例4.13】算法:1 n-1个盘子A柱B柱,借助于C柱;2 第n个盘子A柱C柱;3 n-1个盘子B柱C柱,借助于A柱;其中步骤1
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026中国农业发展银行新疆维吾尔自治区分行纪委办审查调查专业人才社会招聘1人笔试题库【达标题】附答案详解
- 乐山市金口河区考核招聘2026年定向培养乡村振兴紧缺专业大学本科毕业生的考前冲刺密卷附参考答案详解(轻巧夺冠)
- 2026夏季江苏南通市海安市教体系统招聘教师11人笔试题库附参考答案详解(满分必刷)
- 2026年安义县殡葬服务所招聘工作人员20人笔试题库及参考答案详解(培优A卷)
- 2026广西防城港务集团有限公司幼儿园招聘3人考前冲刺密卷及答案详解参考
- 成都市龙泉驿区黄土小学校2026年公开招聘1名编外教师备考题库【夺冠】附答案详解
- 2026年河南省事业单位招聘联考开封市龙亭区考察模拟试卷(A卷)附答案详解
- 2026四川成都市第八人民医院招募医疗卫生辅助岗10人(第二轮招募)备考题库附答案详解(能力提升)
- 2026陕西延安市第九中学招聘12人考前冲刺密卷【轻巧夺冠】附答案详解
- 2026四川雅安市汉源县中医医院招聘医生15人考前冲刺试卷【能力提升】附答案详解
- 高中数学 加练 专题5 第50练 复 数
- 贵州省黔东南州2025-2026学年七年级下学期期末考试英语试卷(含答案)
- SYT 6649-2025《油气管道管体缺陷修复技术规范》
- 2026年秋季新教材统编版九年级上册道德与法治全册知识点背诵提纲精简版
- 2026年高考地理真题山东卷含答案
- 2026中国新材料技术在航空航天领域应用趋势及投资前景报告
- 高支模(盘扣式)监理实施细则
- 连续性肾替代治疗抗菌药物剂量调整专家共识(2026年版)
- 地铁施工请销点流程课件
- 分布式光伏施工劳务承包合同
- 小学英语三年级上册Unit 3单元作业设计
评论
0/150
提交评论