420 likes | 584 Views
第 5 章 函数. 5.1 函数的定义和使用 5.2 嵌套调用 5.3 函数的重载 5.4 变量作用域. 第 5 章 函数. 教学目标 : 1. 掌握函数的定义与调用; 2. 掌握函数参数的传递方式; 3. 掌握变量存储类型的概念及各种存储类型变量的生存期和有效范围; 教学重点 : ( 1 )函数定义、函数调用、函数声明等基本概念; ( 3 )变量的存储类别与作用域。 教学难点 : 变量的作用域. 5.1 函数的定义与调用. 5.1.1 函数概述 5.1.2 函数的定义 5.1.3 函数的调用.
E N D
第5章 函数 • 5.1 函数的定义和使用 • 5.2 嵌套调用 • 5.3 函数的重载 • 5.4 变量作用域
第5章 函数 教学目标: 1.掌握函数的定义与调用; 2.掌握函数参数的传递方式; 3.掌握变量存储类型的概念及各种存储类型变量的生存期和有效范围; 教学重点: (1)函数定义、函数调用、函数声明等基本概念; (3)变量的存储类别与作用域。 教学难点: 变量的作用域
5.1 函数的定义与调用 5.1.1 函数概述 5.1.2 函数的定义 5.1.3 函数的调用
5.1.1 函数概述 函数概念的引入: 通过函数,可以把一个复杂任务分解成为若干个易于解决的小任务。体现逐步细化的设计思想。 入口函数: 组成C++程序的若干函数中,有一个称为main()的函数,它是C++程序执行的入口,它可以调用其他函数,但不可以被调用。而其他一般函数既可以调用也可以被调用。
main ( ) fun1( ) fun2( ) fun3( ) funa( ) funb( ) func( ) 图5.1 函数调用层次关系 5.1.1 函数概述
5.1.1 函数概述 库函数和自定义函数: 库函数或标准函数,是由编译系统预定义的,如一些常用的数学计算函数、字符串处理函数、图形处理函数、标准输入输出函数等。 库函数都按功能分类,集中说明在不同的头文件中。用户只需在自己的程序中包含某个头文件,就可直接使用该文件中定义的函数。 自定义函数: 用户根据需要将某个具有相对独立功能的程序定义为函数,称自定义函数。
5.1.2 函数的定义 无参函数 无参函数定义格式为: 《数据类型》函数名(《void》){函数体} 说明: 数据类型指函数返回值类型,可以是任一种数据类型,没有返回值时应将返回值类型定义为void。 函数名采用合法标识符表示,用户通过使用这个函数名和实参表可以调用该函数。 形式参数表: 对无参函数,参数括号中的void通常省略,但括号不能省略。 函数体由一系列语句组成。函数体可以为空,称为空函数。
5.1.2 函数的定义 例: 打印一个表头 void TableHead ( ) { cout<<″****************″<<endl; cout<<″* example *″<<endl; cout<<″****************″<<endl; }
5.1.2 函数的定义 2 有参函数 有参函数的定义格式为 《数据类型》函数名 (参数类型1 形式参数1《,参数类型2 形式参数2,…》{函数体} 有参函数的参数表中列出所有形式参数的类型和参数名称。各参数即使类型相同也必须分别加以说明。 参数表可以包含任意多项(可以没有),当多于一项时,前后两个参数之间必须用逗号分开。 形式参数简称形参,只能是变量名,不允许是常量或表达式, 例: 返回两个整数中较大一个的值 int max (int a, int b){ return(a>=b?a:b); }
5.1.2 函数的定义 提示 问题:定义函数时究竟哪些变量应当作为函数的参数?哪些应当定义在函数体内? 原则:函数在使用时被看成 “黑匣子”,除了输入输出外,其他部分可不必关心。从函数的定义看出,函数头正是用来反映函数的功能和使用接口,它所定义的是“做什么”。即明确了“黑匣子”的输入输出部分,输出就是函数的返回值,输入就是参数。因此,只有那些功能上起自变量作用的变量才必须作为参数定义在参数表中;函数体中具体描述“如何做”,因此除参数之外的为实现算法所需用的变量应当定义在函数体内。 C++中不允许函数的嵌套定义,即不允许在一个函数中定义另一个函数。
5.1.3 函数的调用 函数调用: 所谓函数调用,就是使程序转去执行函数体。 在C++中,除了主函数外,其他任何函数都不能单独作为程序运行。任何函数功能的实现都是通过被主函数直接或间接调用进行的。 无参函数的调用格式: 函数名( ) 有参函数的调用格式: 函数名(实际参数表) 其中实际参数简称实参,用来将实际参数的值传递给形参,因此可以是常量、具有值的变量或表达式。 【例5.1】 输入两个实数,输出其中较大的数
5.2 函数的参数 5.2.1 函数的参数传递及传值调用 5.2.2 函数返回值 5.2.3 函数声明
5.2.1函数的参数传递及传值调用 参数传递: 函数调用首先要进行参数传递,参数传递的方向是由实参传递给形参。 传递过程是,先计算实参表达式的值,再将该值传递给对应的形参变量。一般情况下,实参和形参的个数和排列顺序应一一对应,并且对应参数应类型匹配(赋值兼容),即实参的类型可以转化为形参类型, 而对应参数的参数名则不要求相同。
5.2.1 函数的参数传递及传值调用 传值调用和引用调用: 按照参数形式的不同,C++有两种调用方式:传值调用和引用调用。传值调用传递的是实参的值,引用调用在第四章介绍过, 本章介绍传值调用。
5.2.1 函数的参数传递及传值调用 传值调用: 将实参的值复制给形参,在函数中参加运算的是形参,而实参不会发生任何改变。传值调用起了一种隔离作用。 【例5.2】实参和形参对应关系的示例。
5.2.2 函数返回值 return语句的格式: return 表达式; 函数的计算结果通过该语句传递回主调函数。 【例5.3】设计函数,根据三角形的三边长求面积。如果不能构成三角形,给出提示信息。 分析:函数为计算三角形面积,一般三角形返回面积值,若不能构成三角形则返回-1。设计一个主函数完成函数测试。根据返回值情况输出相应结果。
5.2.2 函数返回值 讨论: 函数可以有返回值,也可以没有返回值。对于没有返回值的函数,功能只是完成一定操作,应将返回值类型定义为void ,函数体内可以没有return语句,当需要在程序指定位置退出时,可以在该处放置一个: return ;
5.2.3 函数声明 函数声明的引入: 语法上对程序文件中函数的排列次序要求满足先定义后使用。 从结构化程序设计的角度,通常是先调用后定义。使用函数声明,则既符合由粗到精的思维方式,又满足了语法要求。
5.2.3 函数声明 函数声明的格式: 函数声明是一条以分号结束的语句: 《函数返回值类型》函数名 (《形参类型表》); 其中形参表可以逐个列出每个参数的类型和参数名,也可以列出每个形参的类型,参数名可省略,各形参之间以逗号分隔。函数声明和所定义的函数必须在返回值类型、函数名、形参个数和类型及次序等方面完全对应一致,否则将导致编译错误。
下面是一个使用结构化程序设计思想开发的企业管理报表程序的框架。它使用了函数声明。下面是一个使用结构化程序设计思想开发的企业管理报表程序的框架。它使用了函数声明。 void menu_print(); void account_report(); void engineering_report(); void marketing_report(); int main(){ int choice; do{ menu_print(); cin>>choice; }while(choice<=0||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(){ //生成市场报表; }
5.2.3 函数声明 【例5.4】 输出所有满足下列条件的正整数m:10<m<1000且m、m2、m3均为回文数。 分析:回文指左右对称的序列。如121、353等就是回文数。判断整数是否回文数用函数实现,其思想是将该数各位拆开后反向组成新的整数,如果该整数与原数相等则为回文数。 m m*m m*m*m 11 121 1331 101 10201 1030301 111 12321 1367631 运行结果:
5.3 函数的重载 重载的引入: 在C++中,如果需要定义几个功能相似,而参数类型不同的函数,那么这样的几个函数可以使用相同的函数名,这就是函数重载。 例: 求和函数对应不同的参数类型可以定义如下几个重载函数: int sum(int a,int b) double sum(double a,double b) float sum(float a,float b,float c)
例: 编写程序,分别计算下列函数的值(x从键盘输入)
float f1(float x) { float y; y=3*x*x*x+2*x*x-1; return y; } void main(void) { float x, y; cin>>x; y=f1(x); cout<<“x=“<<x<<“ , y=“<<y<<endl; }
编写程序,分别计算下列函数的值(x从键盘输入)编写程序,分别计算下列函数的值(x从键盘输入) 当最后一项小于0.00001时,累加结束。
float fun(float x) { float s=1, t=1; do { t=t/x; s+=t; }while (t>0.00001); return s; } void main(void) { float x; cin>>x; cout<<“s=“<<fun(x)<<endl; }
5.3 函数重载 重载匹配: 当某个函数中调用到重载函数时,编译器会根据实参的类型去对应地调用相应的函数。匹配过程按如下步骤进行: (1)如果有严格匹配的函数,就调用该函数; (2)参数内部转换后如果匹配,调用该函数; (3)通过用户定义的转换寻求匹配。 因此在定义重载函数时必须保证参数类型不同,仅仅返回值类型不同是不行的。函数重载的好处在于,可以用相同的函数名来定义一组功能相同或类似的函数,程序的可读性增强。 【例5.16】 重载函数的应用
5.4 变量作用域 5.4.1 块作用域 5.4.2 函数声明作用域 5.4.3 文件作用域 5.4.4 变量的类型与生命期
5.4 变量作用域 作用域:指标识符能够被使用的范围。只有在作用域内标识符才可以被访问(称为可见)。 本节重点讨论局部域和文件域(全局域),其中局部域包括块域和函数声明域。任何标识符作用域的起始点均为标识符说明处。
5.4.1 块作用域 块的引入: 块指一对大括号括起来的程序段。块中定义的标识符,作用域在块内。 复合语句中定义的标识符, 复合语句是一个块。 作用域仅在该复合语句中。 函数也是一个块。 函数中定义的标识符,包括形参和函数体中定义的局部变量,作用域都在该函数内,也称作函数域。 【例5.7】 输入两数,按从大到小的顺序保存。
5.4.1 块作用域 【例5.8】设计函数完成两数交换,用主函数进行测试。 由VC++平台运行,结果如下: 输入两整数: 3 5 调用前:实参a=3,b=5 调用中… 交换前:形参a=3,b=5 交换后:形参a=5,b=3 调用后:实参a=3,b=5 交换失败 局部变量具有局部作用域使得程序在不同块中可以使用同名变量。这些同名变量各自在自己的作用域中可见,在其它地方不可见。
5.4.1 块作用域 对于块中嵌套其它块的情况,如果嵌套块中有同名局部变量,服从局部优先原则,即在内层块中屏蔽外层块中的同名变量,换句话说,内层块中局部变量的作用域为内层块;外层块中局部变量的作用域为外层除去包含同名变量的内层块部分。 如果块内定义的局部变量与全局变量同名,块内仍然局部变量优先,但与块作用域不同的是,在块内可以通过域运算符“::”访问同名的全局变量。 【例5.9】 显示同名变量可见性。
5.4.2 函数声明作用域 函数声明不是定义函数,在作函数声明时,其中的形参作用域只在声明中,即作用域结束于右括号。正是由于形参不能被程序的其他地方引用,所以通常只要声明形参个数和类型,形参名可省略。
3.文件作用域 5.4.3 文件作用域 文件作用域也称全局作用域。定义在所有函数之外的标识符,具有文件作用域,作用域为从定义处到整个源文件结束。文件中定义的全局变量和函数都具有文件作用域。 如果某个文件中说明了具有文件作用域的标识符,该文件又被另一个文件包含,则该标识符的作用域延伸到新的文件中。如cin和cout是在头文件iostream中说明的具有文件作用域的标识符,它们的作用域也延伸到嵌入iostream的文件中。
5.4.4 存储类型与生存期 C++中关于存储类型的说明符(storage class specifier)有四个:auto、register、static和extern。其中用auto和register修饰的称为自动存储类型,用static修饰的称为静态存储类型,用extern修饰的称为外部存储类型。 1 自动存储类型 自动变量为用auto说明的变量,通常auto缺省。局部变量都是自动变量,生命期开始于块的执行,结束于块的结束,其原因是自动变量的空间分配在栈中,块开始执行时系统自动分配空间,块执行结束时系统自动释放空间。故自动变量的生命期和作用域是一致的。 为提高程序运行效率,可以将某些变量保存在寄存器中,即用register说明为寄存器变量,但不提倡使用。
5.4.4 存储类型与生存期 2 静态存储类型 static说明的变量称为静态变量。根据定义的位置不同,还分为局部静态变量和全局静态变量,也称内部静态变量和外部静态变量。静态变量均存储在全局数据区,如果程序未显式给出初始化值,系统自动初始化为全0,且初始化只进行一次;静态变量占有的空间要到整个程序执行结束才释放,故静态变量具有全局生命期。 局部静态变量是定义在块中的静态变量,当块第一次被执行时,编译系统在全局数据区为其开辟空间并保存数据,该空间一直到整个程序结束才释放。局部静态变量具有局部作用域,但却具有全局生命期。 【例5.10】 自动变量与局部静态变量的区别
5.4.4 存储类型与生存期 3 外部存储类型 一个C++程序可以由多个源程序文件组成。多文件程序系统可以通过外部存储类型的变量和函数来共享某些数据和操作。 在一个程序文件中定义的全局变量和函数缺省为外部的,即其作用域可以延伸到程序的其他文件中。其他文件如果要使用这个文件中定义的全局变量和函数,应该在使用前用“extern”作外部声明。外部声明通常放在文件的开头(函数总是省略extern)。 外部变量声明不同于全局变量定义,变量定义时编译器为其分配存储空间,而变量声明则表示该全局变量已在其他地方定义过,编译系统不再分配存储空间。 【例5.11】外部存储类型的例子
5.4.4 存储类型与生存期 1. 静态生存期 静态生存期(Static extent或Static storage duration)指的是标识符从程序开始运行时就存在,具有存储空间,到程序运行结束时消亡,释放存储空间。具有静态生存期的标识符存放在全局数据区,如全局变量、静态全局变量、静态局部变量。具有静态生存期的标识符在未被用户初始化的情况下,系统会自动将其初始化为0。 函数驻留在代码区,也具有静态生存期。所有具有文件作用域的标识符都具有静态生存期。
5.4.4 存储类型与生存期 2. 局部生存期 在函数内部或块中定义的标识符具有局部生存期(Automatic extent或Automatic storage duration),其生存期开始于执行到该函数或块的标识符定义处,结束于该函数或块的结束处。具有局部生存期的标识符存放在栈区。具有局部生存期的标识符如果未被初始化,其内容是随机的,不可引用。 具有局部生存期的标识符必定具有局部作用域;但反之不然,静态局部变量具有局部作用域,但却具有静态生存期。
5.4.4 存储类型与生存期 3. 动态生存期 具有动态生存期(dynamic extent或dynamic storage duration)的标识符存放在自由存储区,由特定的函数调用或运算来创建和释放,如用new运算符(或调用malloc()函数)为变量分配存储空间时,变量的生存期开始,而用delete运算符(或调用free()函数)释放空间或程序结束时,变量生存期结束。关于new运算符和delete运算符将在以后章节中介绍。
小结: 本章学习了板函数的定义、调用及变量的作用域要求通过本章的学习,掌握函数的定义与调用;掌握变量存储类型的概念及各种存储类型变量的生存期和有效范围 思考题: 1.写一个判断素数的函数,在主函数中由键盘输入一个整数,并给出判断结果。 作业题: 1.编写两个函数,第一个函数返回它的参数平方,第二个函数返回它的立方,要求从主函数输入数据并显示计算结果。