目录
第四章 利用函数实现指定功能
4.1 什么是函数
4.1.1 为什么需要函数
无论是C还是C++,程序中的各项操作基本上都是由函数来实现的,程序编写者要根据需要编写一个个函数,每个函数用来实现某一功能。
4.1.2 函数调用举例
一个函数并不从属于另一个函数,即函数不能嵌套定义,也就是在定义一个函数的过程中又定义另一个函数,也不能把函数的定义部分写在主函数之中。
4.1.3 函数的分类
①系统函数,即库函数
②用户自己定义的函数
(1)无参函数
(2)有参函数
4.2 定义函数的一般形式
4.2.1 定义无参函数的一般形式
定义无参函数的一般形式为:
类型名 函数名([ void ])
{声明部分
执行语句
}
4.2.2 定义有参函数的一般形式
定义有参函数的一般形式为:
类型名 函数名(形式参数表列)
{声明部分
执行部分
}
4.3 函数参数和函数的值
4.3.1 形式参数和实际参数
在定义函数是函数名后面括号中的变量名称为形式参数(formal parameter,简称形参),在主函数中调用一个参数时,函数名后面括号中的参数(可以是一个表达式)称为实际参数(actual parameter,简称实参)。
4.3.2 函数的返回值
通常,希望通过函数的调用时主调函数能得到一个确定的函数值,这就是函数的返回值。
4.4 函数的调用
4.4.1 函数调用的一般形式
1.调用函数的一般形式为:函数名([实参列表])
2.实参与形参按顺序对应,一对一传递数据。但应说明,如果实参表列包括多个实参,对实参求值的顺序并不是确定的
4.4.2 函数调用的方式
1.函数语句
2.函数表达式
c = 2 * max(a,b);
3.函数参数
m = max(a,sqrt(b)); //sqrt(b)时函数调用,其值作为max函数调用的一个实参
4.4.3 对被调用函数的声明和函数声明
1.所谓函数声明(declaration),就是在函数尚未定义的情况下,事先将该函数的有关信息通知表一系统,以便使编译能正常进行。
2.注意:对函数的定义和声明不是同一回事。
定义是指对函数功能的确立,包括指定函数名,函数类型、形参及其类型、函数体,它是一个完整的、独立的函数单位。
声明的作用是把函数的名字、函数类型以及形参的个数、类型和顺序(注意,不包括函数体)通知编译系统,以便在对包含函数的语句进行编译时,据此对其进行对照检查(例如函数名是否正确,实参与形参的类型和个数是否一致)。
3.函数声明的位置可以在调用函数所在的函数中,也可以在函数之外。
4.5 函数的嵌套调用
C++不能嵌套定义函数,但可以嵌套调用函数,也就是说,在调用一个函数的过程中,又调用另一个函数。
4.6 函数的递归调用
在定义一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。
4.7 内置函数
1.函数调用的过程:
①程序先执行函数调用之前的语句;
②流程的控制转移到被调用函数的入口处,同时进行参数传递;
③执行被调用函数中函数体的语句;
④流程返回调用函数的下一条指令处,将函数返回值带回;
⑤接着执行主调函数中未执行的语句;
这样就要求在转去被调用函数之前,要记下当时执行的指令的地址,还要“保护现场”(记下有关当时有关的信息),以便在函数调用之后继续执行。
2.C++提供一种提高效率的方法,即在编译时将所调用函数的代码直接嵌入到主调函数中,而不是将流程转出去。这种嵌入到主调函数中的函数称为内置函数(inline function),又称内嵌函数。
3.对函数作incline声明,只是程序设计者对编译系统提出的一个建议,也就是说它是建议性的,而不是指令性的。并非一经指定为incline,编译系统就必须这样做。编译系统会根据具体情况决定是否这样做。
4.只有那些规模较小而被频繁调用的简单函数,才适用于声明为incline函数。
4.8 函数的重载
1.C++允许用同一函数名定义多个函数,而这些函数的参数个数和参数类型可以不相同。这就是函数的重载(function overloading)。即对一个函数名重新赋予它新的含义,使一个函数名可以多用。所谓重载,其实就是“一物多用”。
2.重载函数的参数个数、参数类型或参数顺序三者中必须至少有一种不同,函数返回值类型可以相同也可以不同。
4.9 函数模板
1.所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。
2.定义函数模板的一般形式为:
template < typename T >
通用函数定义
或
template < class T >
通用函数定义
3.模板只适用于函数体相同、函数的参数个数相同而类型不同的情况,如果参数的个数不同,则不能用函数模板。
4.10 有默认参数的函数
当用同样的实参多次调用同一函数时,C++提供了简单的处理方法,给形参一个默认值,这样形参就不必一定要从实参取值了。
4.11 局部变量和全局变量
每一个变量都有其有效作用范围,这就是变量的作用域。
4.11.1 局部变量
1.在一个函数内部定义的变量是内部变量,他只是本函数范围内有效,也就是说只有在本函数内才能使用他们,在此函数以外是不能使用这些变量的。
2.在复合语句中定义的变量只是在本复合语句范围内有效。这些内部变量称为局部变量。
4.11.2 全局变量
1.在函数内定义的变量是局部变量,而在函数之外定义的变量是外部变量,称为全局变量。全局变量的有限范围为从定义变量的位置开始到本源文件结束。
2.变量的有效范围称为变量的作用域(scope)。归纳起来,变量有4种不同的作用域:文件作用域(file scope)、函数作用域(function scope)、块作用域(block scope)和函数原型作用域(function prototype scope)。文件作用域是全局的,其他三者是局部的。
4.12 变量的存储类别
4.12.1 动态存储方式与静态存储方式
1.变量还有另一种属性——存储器(storage duration,也称生命期)。存储期是指变量在内存中的存在周期。这是从变量值存在的时间角度来分析的。存储器可以分为静态存储期(static storage duration)和动态存储期(auto)。这是由变量的静态存储方式和动态存储方式决定的。
2.所谓静态存储方式是指在程序运行期间,系统对变量分配固定的存储时间。而动态存储方式则是在程序运行期间,系统对变量动态地分配存储空间。
3.内存中的供用户使用的存储空间的情况。这个存储空间可以分为3部分,即
①程序区 |
②静态存储区 |
③动态存储区 |
程序中所用的程序分别存放在静态存储区和动态存储区中。全局变量全部存放在静态存储区中,在程序开始执行时给全局变量分配存储单元,程序执行完毕就释放这些空间。在程序执行过程中它们占据固定的存储单元,而不是动态地进行分配和释放。
3.在动态存储区中存放以下数据:
①函数形式参数。在调用函数时给形参分配存储空间。
②函数中定义的变量(未加static声明的局部变量)
③函数调用时的现场保护和返回地址等。
4.12.2 自动变量
函数中的局变量,如果不用关键字static加以声明,编译系统对他们是动态地分配存储空间的。函数的形参和在函数中定义的变量(包括在复合语句中定义的变量),都属此类。在调用该函数时,系统给形参和函数中定义的变量分配存储空间,数据存储在动态存储区中。在函数调用结束时就自动释放这些空间。如果是在复合语句中定义的变量,则在变量定义时分配存储空间,在复合语句结束时自动释放空间。因此这类局部变量称为自动变量。自动变量用关键字auto作存储类别的声明。
4.12.3 用static声明静态局部变量
1.对静态局部变量的说明:
①静态局部变量在静态存储区内分配存储单元。在程序整个运行期间都不释放。而自动变量(即动态局部变量)属于动态存储类别,存储在动态存储空间(而不是静态存储空间),函数调用结束后即释放。
②对静态函数变量是在编译时赋初值的,即只赋初值一次,在程序运行时它已有初值。以后每次调用函数时不再重新赋初值而只是保留上一次函数调用结束时的值。而对自动变量赋初值,不是在编译时进行的,而是在函数调用时进行,每调用一次函数重新给一次初值,相当于执行一次赋值语句。
③如果在定义局部变量时不赋初值的话,对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符(对字符变量)。而对自动变量来说,如果不赋初值,则他的值是一个不确定的值。这是由于每次函数调用结束后存储单元已释放,下次调用时又重新另外分配单元,而所分配的单元中的值时不确定的。
④虽然静态局部变量在函数调用结束后仍然存在,但其他函数是不能引用他的,也就是说,在其他函数中它是“不可见的“。
4.12.4 用register声明寄存器变量
4.12.5 用extern声明外部变量
1.在一个文件内声明全局变量
2.在多文件的程序中声明外部变量
4.12.3 用static声明静态外部变量
4.13 变量属性小姐
一个变量除了数据类型以外,还有3种属性:(P116)
①存储类别:C++允许使用auto,static,register,extern 4种存储类别。
②作用域:指在程序中可以引用该变量的区域。
③存储期:指变量在内存的存储周期。
4.14 关于变量的声明和定义
1.函数的声明是函数的原型,而函数的定义是函数功能上的确立。
2.对变量而言,生命和定义的关系稍微复杂一些。在声明部分出现的变量有两种情况:一种是需要建立存储空间的(如int a;):另一种是不需要建立存储空间的(如extern int a;)。前者称为定义性声明,或者简称定义。后者称为引用性声明。
3.一般为了叙述方便,把建立存储空间的声明称为定义,而把不需要建立存储空间的声明称为声明。
4.15 内部函数和外部函数
4.15.1 内部函数
如果一个函数只能被本文件中其他函数所调用,他称为内部函数。
4.15.2 外部函数
①在定义函数时,如果在函数首部的最左端冠以关键字extern,则表示此函数时外部函数,可供其他文件调用。
②在需要调用此函数的文件中,用extern声明所用的函数是外部函数。
4.16 头文件
4.16.1 头文件的内容
许多程序都要使用系统提供的库函数,而C++又规定在调用函数前必须对被调用的函数作原型声明,如果由用户来完成这些工作,是非常麻烦和枯燥的,而且容易遗漏和出错。现在,库函数的开发者把这些信息写在一个文件中,用户只须将该文件”包含“进来即可(如调用数学函数的,应包含cmath文件),者就大大简化了程序,写一行#include指令的作用相当于写十几行、几百行甚至更多行的内容。这种常用在文件头部的被包含的文件称为“标题文件”或“头部文件”(“头文件”)
①对类型的声明
②函数声明:函数的定义是不放在头文件中的,而是放在函数库中或单独编译成目标文件,在编译链接阶段与用户文件连接组成可执行文件
③内置(incline)函数的定义:由于内置函数的代码是要插入用户程序中的(内联函数),因此它应当与调用它的语句在同一文件中,而不能分别在不同的文件中。
④宏定义:用#define定义的符号常量和const声明的常变量
-const变量默认是 内部链接属性
-非const变量默认是 外部链接属性
-如果要将一个const变量设置为外部链接属性,需要使用extern关键字,extern const int value = 10;如果要将一个非const变量设置为内部链接属性,需要是用static关键字,static int value = 10;
⑤全局变量定义
⑥外部变量声明:“extern int a;”
4.16.2 关于C++标准库和头文件的形式
新的C++标准库中的头文件不在包括.h,例如:#include <string>
但为了使大批已有的C程序能继续使用,许多C++编译系统保留了C的头文件,如C++中提供的cmath头文件,其中第一个字母c表示它是继承C的头文件,也就是说,C++提供两种不同形式的头文件,由程序设计者选用
#include <cmath> //C++形式的头文件
#include <math.h> //C++形式的头文件