




已阅读5页,还剩44页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
在结构化程序设计中,函数是将任务进行模块划分的基本单位。一个函数实现一项功能。 在面向对象程序设计中,函数是对数据的一项操作,也是实现一项功能。,第四章 函数,要掌握函数的使用,必须理解函数调用时的内部实现机制,以及与此相关的内存分配机制、变量生命期和作用域。,本章还将介绍关于函数重载的概念,介绍递归算法、内联函数、默认参数函数以及编译预处理、工程文件的概念和运行库函数。,第4章 函数,4.1 函数的定义与调用,4. 5 作用域与标识符的可见性,4.4 函数调用机制,4.3 全局变量和局部变量,4.2 函数的参数传递, 返回值及函数声明,4.8 编译预处理,4.6 存储类型与标识符的生命期,4.7 函数的递归调用,4.1 函数的定义与调用,4.1.1 函数概述,4.1.2 函数的定义,4.1.3 函数的调用,4.1.1 函数概述,函数是C+程序的基本组成模块。,通过函数,可以把一个复杂任务分解成为若干个易于解决的小任务。充分体现逐步细化的设计思想。,组成C+程序的若干函数中,有一个称为main()(Winmain())函数,是程序执行的入口,它可以调用其他函数,但不可以被调用。而其他一般函数既可以调用也可以被调用。,函数概念的引入:,入口函数:,4.1.1 函数概述,4.1.1 函数概述,库函数和自定义函数: 库函数或标准函数,是由编译系统预定义的,如一些常用的数学计算函数、字符串处理函数、图形处理函数、标准输入输出函数等。 库函数都按功能分类,集中说明在不同的头文件中。用户只需在自己的程序中包含某个头文件,就可直接使用该文件中定义的函数。 用户根据需要将某个具有相对独立功能的程序定义为函数,称自定义函数。,4.1.2 函数的定义,无参函数定义格式为: 数据类型 函数名(void)函数体,说明: 数据类型指函数返回值类型,可以是任一种数据类型,默认为返回整型值(但新标准要求写明,不用默认方式)。没有返回值应将返回值类型定义为void。 函数名采用合法标识符表示。 对无参函数,参数括号中的void通常省略,但括号不能省略。 函数体由一系列语句组成。函数体可以为空,称为空函数。,1 无参函数,4.1.2 函数的定义,/打印一个表头 void TableHead() cout*endl; cout* example *endl; cout*endl; ,int input ( ) /输入满足要求的数据 int n; coutn; while(n=5); return n; ,2 有参函数,有参函数的定义格式为 数据类型 函数名 (参数类型1 形式参数1 ,参数类型2 形式参数2, 函数体,/返回两个整数中的较大值 int max (int a, int b) return(a=b?a:b); ,有参函数的参数表中列出所有形式参数的类型和参数名称。各参数即使类型相同也必须分别加以说明。 形式参数简称形参,只能是变量名,不允许是常量或表达式。,void delay(long n) int i; for (i=0;in;i+); /延时一段时间,问题:定义函数时究竟哪些变量应当作为函数的参数?哪些应当定义在函数体内?,4.1.3 函数的调用,函数调用: 所谓函数调用,就是使程序转去执行函数体。 在C+中,除了主函数外,其他任何函数都不能单独作为程序运行。任何函数功能的实现都是通过被主函数直接或间接调用进行的。 无参函数的调用格式: 函数名( ) 有参函数的调用格式: 函数名(实际参数表) 其中实际参数简称实参,用来将实际参数的值传递给形参,因此可以是常量、具有值的变量或表达式。,【例4.1】 输入两个实数,输出其中较大的数,4.1.3 函数的调用【例4.1】,main( )函数,调用max(2.5,4.7 ),函数max(2.5,4.7 ),return 4.7,主程序后续语句,【例4.1】 输入两个实数,输出其中较大的数。其中求两个实数中的较大数用函数完成。 程序如下: #include float max(float a,float b) return(a=b?a:b); int main() float x,y; coutxy; coutx“和“y“中较大数为“max(x,y)endl; return 0; ,4.2 函数的参数传递、返回值及 函数声明,4.21 函数的参数传递及传值调用,4.23 函数声明,4.22 函数返回值,参数传递: 函数调用首先要进行参数传递,参数传递的方向是由实参传递给形参。 传递过程是,先计算实参表达式的值,再将该值传递给对应的形参变量。一般情况下,实参和形参的个数和排列顺序应一一对应,并且对应参数应类型匹配(赋值兼容),即实参的类型可以转化为形参类型。而对应参数的参数名则不要求相同。总而言之:参数要求位置匹配,4.2.1 函数的参数传递及传值调用,传值调用和引用调用: 按照参数形式的不同,C+有两种调用方式:传值调用和引用(传址)调用。传值调用传递的是实参的值,这里先介绍传值调用。,4.2.1 函数的参数传递及传值调用,传值调用: 将实参的值复制给形参,在函数中参加运算的是形参,而实参不会发生任何改变。传值调用起了一种隔离作用。,【例4.2】 实参和形参对应关系的示例。,4.2.1 函数的参数传递及传值调用【例4.2】,调用power(4.6,3 ),函数power(4.6,3 ),return 97.336,主程序后续语句,【例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; cout“power(“x,n“)=“power(x,n)endl; cout“power(“c,n“)=“power(c,n)endl; cout“power(“n,x“)=“power(n,x)endl; return 0; ,4.2.2 函数返回值,对于有返回值的函数,在函数的出口处必须用return语句将要返回的值返回给调用者。 return语句的格式: return 表达式; 其中表达式的值即函数要返回的值。执行该语句时,首先计算表达式的值,然后将其转化为返回值类型所规定的类型返回,同时结束函数的执行,返回到调用处继续执行。与函数调用一样,返回也有引用方式。,【例4.3】设计函数,根据三角形的三边长求面积。如果不能构成三角形,给出提示信息。 分析:函数为计算三角形面积,一般三角形返回面积值,若不能构成三角形则返回-1。设计一个主函数完成函数测试。根据返回值情况输出相应结果。,【例4.3】程序:已知三角形边长求面积,#include #include float TriangleArea(float a, float b, float c) if (a+babc; area=TriangleArea(a,b,c); if(area=-1) cout(a,b, c)“不能构成三角形!“endl; else cout“三角形(“a,b,c “)面积为:“areaendl; return 0; ,4.2.2 函数返回值,讨论: 函数可以有返回值,也可以没有返回值。对于没有返回值的函数,功能只是完成一定操作,应将返回值类型定义为void ,函数体内可以没有return语句,当需要在程序指定位置退出时,可以在该处放置一个: return ; 返回值是如何返回到调用处的?实际上,在函数返回时,系统会在内存建立一个临时变量,函数返回时将函数值保存在该临时变量中,然后由主调函数中包含调用的表达式语句从该临时变量中取得值,表达式语句执行后该临时变量撤销。,4.2.3 函数声明,函数声明是一条以分号结束的语句: 函数返回值类型 函数名 (形参表);,语法上对程序文件中函数的排列次序要求满足先定义后使用。对于函数,只要在调用之前作函数声明(Function Declaration),则函数定义放在任何位置程序都能正确编译运行。,其中形参表可以逐个列出每个参数的类型和参数名,也可以列出每个形参的类型,参数名可省略,各形参之间以逗号分隔。函数声明和所定义的函数必须在返回值类型、函数名、形参个数和类型及次序等方面完全对应一致,否则将导致编译错误。,函数声明的引入:,函数声明的格式:,4.2.3 函数声明,例如【例4.3】中求三角形面积的函数声明为: float TriangleArea(float a,float b,float c); 或 float TriangleArea(float,float,float); 但下面的函数原型是错误的: int TriangleArea (float,float,float); /错误,返回值类型不同 float TriangleArea(int,int,int); /错误,参数类型不同 float TriangleArea(float,float); /错误,参数个数不同 按照由粗到精、逐步细化的结构化程序设计思想,程序设计一般从主函数入手,其中某些功能用函数调用语句完成,描述出程序的总体功能框架;然后再逐层深入,完成每个函数的具体实现。按此思路设计出的程序必然将主函数安排在前,而函数定义安排在后,但这样不符合先定义后使用的原则,编译无法通过,用函数声明则可解决这一问题。,下面是一个使用结构化程序设计思想开发的企业管理报表程序的框架。它使用了函数声明。 void menu_print(); void account_report(); void engineering_report(); void marketing_report(); int main() int choice; do menu_print(); cinchoice; while(choice=4); switch(choice) case 1: account_report(); break; case 2: engineering_report(); break; case 3: marketing_report(); break; return 0; ,void menu_print() cout”系统功能:”endl; cout”1 财务报表”endl; cout”2 工程报表”endl; cout”3 市场报表”endl; cout”选择业务序号:”; void account_report() /生成财务报表 void engineering_report() /生成工程报表 void marketing_report() /生成市场报表; ,4.2.3 函数声明,【例4.4】 输出所有满足下列条件的正整数m:10m1000且m、m2、m3均为回文数。,分析:回文指左右对称的序列。如121、353等就是回文数。判断整数是否回文数用函数实现,其思想是将该数各位拆开后反向组成新的整数,如果该整数与原数相等则为回文数。,m m*m m*m*m 11 121 1331 101 10201 1030301 111 12321 1367631,运行结果:,4.3 全局变量和局部变量,4.3.1 变量的存储机制与C+的内存布局,4.3.2 全局变量,4.3.3 局部变量,4.3.1 变量的存储机制与C+的内存布局,自由存储区 (动态数据),操作系统为一个C+程序的运行所分配的内存分为四个区域,如图4.3 所示:,存储区域说明: (1)代码区(Code area):存放程序代码,即程序中各个函数的代码块; (2)全局数据区(Data area):存放全局数据和静态数据;分配该区时内存全部清零,结果变量的所有字节等效初始化为全。 (3)栈区(Stack area):存放局部变量,如函数中的变量等;分配栈区时不处理内存,即变量取随机值。 (4)自由存储区(Free store area):存放与指针相关的动态数据。分配自由存储区时不处理内存。,4.3.1 变量的存储机制与C+的内存布局,4.3.2 全局变量,在所有函数之外定义的变量称为全局变量。,全局变量存放在全局数据区,因编译器自动将该区清为全0,如果用户在定义时不显式给出初始化值,则等效初始化为全0 。,全局变量可定义在程序开头,也可定义在中间位置,该全局变量在定义处之后的任何位置都是可以访问的,称为可见的。,【例4.5】 多个函数使用全局变量的例子。,全局变量引入:,4.3.2 全局变量【例4.5】,打印 200,调用 func( ),函数 func( ),200*2 =400,打印 400,n=100,n=100*2 =200,【例4.5】 多个函数使用全局变量的例子。 int n=100; void func() n*=2; int main() n*=2; coutnendl; func(); coutnendl; return 0;,4.3.3 局部变量,定义在函数内或块内的变量称为局部变量。,程序中使用的绝大多数变量都是局部变量。,局部变量在程序运行到它所在的块时建立在栈中,该块执行完毕局部变量占有的空间即被释放。故亦称为自动变量。,局部变量在定义时可加修饰词auto,但通常省略。局部变量在定义时若未初始化,其值为随机数。,局部变量引入:,【例4.6】 使用局部变量的例子。,4.3.3 局部变量,打印main()中的t=4.5,调用 fun( ),函数 fun( ),打印fun()中的t=5,打印main()中的t=4.5,t = 5,【例4.6】 使用局部变量的例子。 void fun() auto int t=5; / fun()中的局部变量,auto可省略 cout“fun()中的t=“tendl; int main() float t=4.5; /main()函数中的局部变量 cout“main()中的t=“tendl; fun(); cout“main()中的t=“tendl; return 0;,4.4 函数调用机制,局部变量占用的内存是在程序执行过程中“动态”地建立和释放的。这种“动态”是通过栈由系统自动管理进行的。,(1)建立栈空间;,(6)恢复现场:取主调函数运行状态及返回地址,释放栈空间;,(7)继续主调函数后续语句。,(5)释放被调函数中局部变量占用的栈空间;,(4)执行被调函数函数体;,(3)为被调函数中的局部变量分配空间,完成参数传递;,(2)保护现场:主调函数运行状态和返回地址入栈;,调用过程:,4.4 函数调用机制,void fun1(int, int); void fun2(float); int main() int x=1;y=2; fun1(x, y); return o; void fun1(int a,int b) float x=3; fun2(x); void fun2(float y) int x; ,此图例说明在程序执行过程中怎样通过栈“动态”地建立和释放局部变量占用的内存的,4.5 作用域与标识符的可见性,3 文件域,2 函数声明域,作用域:指标识符能够被使用的范围。只有在作用域内标识符才可以被访问(称为可见)。,本节重点讨论局部域和文件域(全局域),其中局部域包括块域和函数声明域。任何标识符作用域的起始点均为标识符说明处。,下面分别介绍:,1 块域,函数中定义的标识符,包括形参和函数体中定义的局部变量,作用域都在该函数内,也称作函数域。,块域,块指一对大括号括起来的程序段。块中定义的标识符,作用域在块内。,复合语句是一个块。,函数也是一个块。,复合语句中定义的标识符,,作用域仅在该复合语句中。,【例4.7】 输入两数,按从大到小的顺序保存。,块的引入:,块域【例4.7】,3,5,a=3 b=5,a=5 b=3,【例4.7】 输入两数,按从大到小的顺序保存,并输出结果。,结果,栈,t,= 3,int main() int a,b; /具有函数域 coutab; cout=a) int t; /具有块域 t=a; a=b; b=t; /交换a,b的值 cout“a=“at“b=“bendl; return 0;,上述程序若在最后一个cout语句处增加: couttendl; 则编译时会提示错误,因为变量t的作用域只在if语句中,其它地方不可见。,块域,由VC+运行,结果如下: 输入两整数: 3 5 调用前:实参a=3,b=5 调用中 交换前:形参a=3,b=5 交换后:形参a=5,b=3 调用后:实参a=3,b=5 交换失败,局部变量具有局部作用域使得程序在不同块中可以使用同名变量。这些同名变量各自在自己的作用域中可见,在其它地方不可见。,【例4.8】设计函数完成两数交换,用主函数进行测试。,【例4.8】设计函数完成两数交换,用主函数进行测试。 #include void swap(int,int); int main() int a,b; /a,b作用域为main() coutab; 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“调用中“endl; cout“交换前:形参a=“a,“b=“bendl; int t; t=a; a=b; b=t; /交换swap()中的a,b的值 cout“交换后:形参a=“a,“b=“bendl; ,块域,对于块中嵌套其它块的情况,如果嵌套块中有同名局部变量,服从局部优先原则,即在内层块中屏蔽外层块中的同名变量,换句话说,内层块中局部变量的作用域为内层块;外层块中局部变量的作用域为外层除去包含同名变量的内层块部分。,如果块内定义的局部变量与全局变量同名,块内仍然局部变量优先,但与块作用域不同的是,在块内可以通过域运算符“:”访问同名的全局变量。,【例4.9】 显示同名变量可见性。,200 300,内 i= 500,内 j= 600,内n=500+600 =1100,1100 500 600,100,200+300=500,500,500 200 300,外部 i=200,外部 j=300,【例4.9】 显示同名变量可见性。 int n=100; #include int main() int i=200,j=300; cout ntitjendl; /内部块 int i=500,j=600,n; n=i+j; cout ntitj endl; /输出局部变量n cout:nendl;/输出全局变量n n=i+j; /修改全局变量 cout ntitj endl; return 0;,函数声明域,函数声明不是定义函数,在作函数声明时,其中的形参作用域只在声明中,即作用域结束于右括号。正是由于形参不能被程序的其他地方引用,所以通常只要声明形参个数和类型,形参名可省略。,3 文件域,文件域也称全局域。定义在所有函数之外的标识符作用域为从定义处到整个源文件结束,即文件域 。文件中定义的全局变量和函数的作用域为文件域。 如果某个文件中说明了作用域为文件域的标识符,该文件又被另一个文件包含,则该标识符的作用域延伸到新的文件中。如cin和cout是在头文件iostream中说明的具有文件作用域的标识符,它们的作用域也延伸到嵌入iostream的文件中。,存储类型(storage class)决定标识符的存储区域,即编译系统在不同区域为不同存储类型的标识符分配空间。由于存储区域不同,标识符的生命期也不同。所谓生命期,指的是标识符从获得空间到空间释放之间的期间,标识符只有在生存期中、并且在其自己的作用域中才能被访问。,4.6 存储类型与标识符的生命期,4.6.1 存储类型,4.4.2 生命期,自动变量为用auto说明的变量,通常auto缺省。局部变量都是自动变量,生命期开始于块的执行,结束于块的结束,其原因是自动变量的空间分配在栈中,块开始执行时系统自动分配空间,块执行结束时系统自动释放空间。故自动变量的生命期和作用域是一致的。,4.6.1 存储类型,为提高程序运行效率,可以将某些变量保存在寄存器中,即用register说明为寄存器变量,但不提倡使用。,C+中关于存储类型的说明符(storage class specifier)有四个:auto、register、static和extern。其中用auto和register修饰的称为自动存储类型,用static修饰的称为静态存储类型,用extern修饰的称为外部存储类型。,1 自动存储类型,static说明的变量称为静态变量。根据定义的位置不同,还分为局部静态变量和全局静态变量,也称内部静态变量和外部静态变量。静态变量均存储在全局数据区,如果程序未显式给出初始化值,则等效初始化为全0;静态变量占有的空间要到整个程序执行结束才释放,故静态变量具有整个程序执行期间的生命期。,4.6.1 存储类型,局部静态变量是定义在块中的静态变量,编译系统在全局数据区为其开辟空间并保存数据,该空间一直到整个程序结束才释放。局部静态变量具有局部作用域,但却具有整个程序执行期间的生命期。如果显式给出初始化值,则在该块第一次执行时完成,且只进行一次。,2 静态存储类型,【例4.10】 自动变量与局部静态变量的区别,4.6.1 存储类型,3 外部存储类型,一个C+程序可以由多个源程
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年三硼酸锂(LBO)晶体项目建议书
- 信托投资合同样本
- 劳动局合同模板
- 股权转让顾问协议二零二五年
- 二零二五厦门二手房买卖合同大全
- 房屋抵押协议书二零二五年
- 个人猪场转让合同
- 二零二五版冷静期离婚协议书
- 家庭宽带业务协议
- 知识产权共有协议二零二五年
- 2025届贵州省安顺市高三二模语文试题
- 市政道路电力、照明、通信管道工程施工方案方案
- 球的体积和表面积说课稿
- GB/T 30726-2014固体生物质燃料灰熔融性测定方法
- 可吸收丝素修复膜(CQZ1900597)
- 凯莱通综合版
- 步行功能训练详解课件
- 几内亚共和国《矿产法》
- 物理讲义纳米光子学
- 保洁服务礼仪培训(共55张)课件
- 中考英语写作指导课件(共41张PPT)
评论
0/150
提交评论