第5单元函数 第5单元函数 本单元教学目标 介绍C++函数的定义、声明和调用方法。 学习要求 熱练掌握C艹+函数的编写和调用方法,以及内联函数、函数重载和递归函数的概念 授课内容 函数是C++程序的构成基础。C++程序都是由一个个函数所组成的,即便是最简单的程 序,也得有一个main()函数。因此,一个C++程序无论多么复杂,规模有多么大,程序 的设计最终都落实到一个个函数的设计和编写上 在C++中,函数是构成程序的基本模块,每个函数具有相对独立的功能。C++的函数 有三种:主函数(即main()函数)、C++提供的库函数和用户自己定义的函数 合理地编写用户自定义函数,可以简化程序模块的结构,便于阅读和调试,是结构化程 序设计方法的主要内容之 5定义和调用函数 一个函数必须先定义后才能使用。所谓定义函数,就是编写完成函数功能的程序块。定 义函数的一般格式为 () 其中: 1.函数值类型声明:即调用该函数后所得到的函数值的类型。例如,例1-2中的函数 gra()的函数值的类型是 double,即双精度浮点类型。函数值是通过函数体内部的 return 语句提供的,其格式为: return 功能是将表达式的值作为函数值返回。在编写函数时要注意用 return语句提供的函数值的类
第 5 单元 函数 - 87 - 第 5 单元 函数 本单元教学目标 介绍C++函数的定义、声明和调用方法。 学习要求 熟练掌握C++函数的编写和调用方法,以及内联函数、函数重载和递归函数的概念。 授课内容 函数是 C++程序的构成基础。C++程序都是由一个个函数所组成的,即便是最简单的程 序,也得有一个 main()函数。因此,一个 C++程序无论多么复杂,规模有多么大,程序 的设计最终都落实到一个个函数的设计和编写上。 在C++中,函数是构成程序的基本模块,每个函数具有相对独立的功能。C++的函数 有三种:主函数(即 main()函数)、C++提供的库函数和用户自己定义的函数。 合理地编写用户自定义函数,可以简化程序模块的结构,便于阅读和调试,是结构化程 序设计方法的主要内容之一。 5.1 定义和调用函数 一个函数必须先定义后才能使用。所谓定义函数,就是编写完成函数功能的程序块。定 义函数的一般格式为: () { } 其中: 1.函数值类型声明:即调用该函数后所得到的函数值的类型。例如,例 1-2 中的函数 grav()的函数值的类型是 double, 即双精度浮点类型。函数值是通过函数体内部的 return 语句提供的,其格式为: return ; 功能是将表达式的值作为函数值返回。在编写函数时要注意用 return 语句提供的函数值的类
第5单元函数 型应与函数声明中的函数值类型一致,否则可能出现错误。 有些函数可能没有函数值,或者说其函数值对调用者来说是不重要的。这时调用该函数 实际上是为了得到运行该函数内部的程序段的其他效果。这一点与数学中的函数概念有所不 同,需特别注意。如果要声明一个函数确实没有返回值,可以使用声明符void。例如主函数 void maino 既没有返回值,也不需要参数。对于一个被声明为void类型的函数,编译程序如果发现在 程序中用到了其返回值,或者在该函数中出现了有返回值的 retum语句,都会报告相应的错 误信息,便于检查程序是否有错 2.参数声明:C++函数的参数声明格式为: ,参数 例如 int array[, int count 3.函数体:函数体本身是一个分程序,由语句和其他分程序组成。C++语句以分号“;” 结束,一行上可以书写多个语句,一个语句也可以分开写在连续的若干行上(但名字、语句 标识符等不能跨行书写)。C++的语句可以分为声明语句和执行语句两类,在一个函数体(或 分程序中)这两种语句可以交替出现,但对某具体变量来说,应先声明,后使用 例5-1编写一个求阶乘n!的函数。 算法:阶乘n的定义为: n!=n×(n-1)×(n-2)×…×2×1 且规定0!=1。 程序 / Example5-1:计算阶乘n! #include //函数facO:计算阶乘 long fac(int n return lse if (n==o) return IL while(n>1)
第 5 单元 函数 - 88 - 型应与函数声明中的函数值类型一致,否则可能出现错误。 有些函数可能没有函数值,或者说其函数值对调用者来说是不重要的。这时调用该函数 实际上是为了得到运行该函数内部的程序段的其他效果。这一点与数学中的函数概念有所不 同,需特别注意。如果要声明一个函数确实没有返回值,可以使用声明符 void。例如主函数 void main() { … … } 既没有返回值,也不需要参数。对于一个被声明为 void 类型的函数,编译程序如果发现在 程序中用到了其返回值,或者在该函数中出现了有返回值的 return 语句,都会报告相应的错 误信息,便于检查程序是否有错。 2.参数声明:C++函数的参数声明格式为: ,,…, 例如 int array[],int count 3.函数体:函数体本身是一个分程序,由语句和其他分程序组成。C++语句以分号“;” 结束,一行上可以书写多个语句,一个语句也可以分开写在连续的若干行上(但名字、语句 标识符等不能跨行书写)。C++的语句可以分为声明语句和执行语句两类,在一个函数体(或 分程序中)这两种语句可以交替出现,但对某具体变量来说,应先声明,后使用。 [例 5-1] 编写一个求阶乘 n!的函数。 算 法:阶乘 n!的定义为: n! = n×(n−1)×(n−2)×...×2×1 且规定 0! = 1。 程 序: // Example 5-1:计算阶乘 n! #include // 函数 fac():计算阶乘 long fac(int n) { long result = 1L; if(n1) {
第5单元函数 result k= n. return result 分析:因为即使n的数值并不大(例如n=10),其阶乘值就可能超出int型数的表 示范围。因此我们将fac()函数的函数值类型定为long类型。如果n为负数,则函数fac ()返回-1L,负值在正常的阶乘值中是不会出现的,正好用作参数错误的标志。 该函数定义了一个阶乘的算法。该函数一经定义,就可以在程序中多次地使用它。函数 的使用是通过函数调用来实现的。 在C++程序中,除了main()函数以外,任何一个函数都不能独立地在程序中存在 任一函数的执行都是通过在main()函数中直接或间接地调用该函数来引发的。调用一个 函数就是执行该函数之函数体的过程。 函数调用的一般形式为 () 函数的调用既可以出现在表达式可出现的任何地方,也可以以函数调用语句(后加分号) 的形式独立出现。实参表是调用函数时所提供的实在参数值,这些参数值可以是常量、变量 或者表达式。调用函数时提供给函数的实参应该与函数的参数表中的参数的个数和类型 对应。特别应该注意,C++中实参与参数变量之间数据的传递是按照“值传递”的方式进 行的,函数的参数实际上是定义于函数中的局部变量,在调用函数时由实参为这些参数变量 提供初值 例5-2阶乘函数的调用 程序 ∥/ Example5-2:测试阶乘计算函数的主程序 void main o int n cout < Please input a number n to calculte n!: cout <<n <<"!="<< fac(n)<< endl a Please input a number n to calculte n!: 5 出:5!=1 52函数原型 C++规定,函数和变量一样,在使用之前也应该事先声明。函数的定义可视为对函数
第 5 单元 函数 - 89 - result *= n; n--; } return result; } 分 析:因为即使 n 的数值并不大(例如 n = 10),其阶乘值就可能超出 int 型数的表 示范围。因此我们将 fac()函数的函数值类型定为 long 类型。如果 n 为负数,则函数 fac ()返回−1L,负值在正常的阶乘值中是不会出现的,正好用作参数错误的标志。 该函数定义了一个阶乘的算法。该函数一经定义,就可以在程序中多次地使用它。函数 的使用是通过函数调用来实现的。 在 C++程序中,除了 main()函数以外,任何一个函数都不能独立地在程序中存在。 任一函数的执行都是通过在 main()函数中直接或间接地调用该函数来引发的。调用一个 函数就是执行该函数之函数体的过程。 函数调用的一般形式为: () 函数的调用既可以出现在表达式可出现的任何地方,也可以以函数调用语句(后加分号) 的形式独立出现。实参表是调用函数时所提供的实在参数值,这些参数值可以是常量、变量 或者表达式。调用函数时提供给函数的实参应该与函数的参数表中的参数的个数和类型一一 对应。特别应该注意,C++中实参与参数变量之间数据的传递是按照“值传递”的方式进 行的,函数的参数实际上是定义于函数中的局部变量,在调用函数时由实参为这些参数变量 提供初值。 [例 5-2] 阶乘函数的调用。 程 序 // Example 5-2:测试阶乘计算函数的主程序 void main() { int n; cout > n; cout << n << "! = " << fac(n) << endl; } 输 入:Please input a number n to calculte n! :5 输 出:5! = 120 5.2 函数原型 C++规定,函数和变量一样,在使用之前也应该事先声明。函数的定义可视为对函数
第5单元函数 的声明。因此,在前面各单元的例子中,函数的定义均放在程序的前部。另外,在C+中还 有一种函数的引用性声明,即函数原型( Function Prototype),通常也称其为函数声明。函 数原型的一般形式为 () 其中各部分的意义与函数定义相同。 函数原型与函数定义的区别在于:函数原型没有函数体部分,且是用分号结束的,就像 变量的声明 有了函数原型,则只要将其放在对函数的调用之前,则即使函数的定义放在其引用之后, 也不会引起编译失败。这就为进行结构化、模块化程序设计提供了极大的方便。 例5-3使用函数原型。 ∥/ Example5-3:求两数中的大数 Include void maino int max(int x, int y) cout > a>b cout y?X: y 分析:尽管函数max()的定义出现在对它的调用之后,然而由于使用了函数原型 程序就能成功地编译通过。函数原型向编译器提供了函数的名字、值的类型和参数的个数及 类型等信息。在函数原型中,参数的名字也可以省略,因此,上例中的函数原型也可以改写 nt max (int, int ): 53函数间的参数传递 在C艹程序中,利用函数不仅可以改善程序的可读性,而且提高了程序的灵活性。由 于函数通常是用于实现一个具体功能的模块,所以它必然要和程序中的其他模块交换信息。 实际上,一个函数可以从函数之外获得一些数据,并可向其调用者返回一些数据。这些数据 主要是通过函数的参数与函数的返回值来传递的 函数可以没有参数,也可以有一个或多个参数。在参数表中声明的参数(变量)叫做函
第 5 单元 函数 - 90 - 的声明。因此,在前面各单元的例子中,函数的定义均放在程序的前部。另外,在 C++中还 有一种函数的引用性声明,即函数原型(Function Prototype),通常也称其为函数声明。函 数原型的一般形式为 (); 其中各部分的意义与函数定义相同。 函数原型与函数定义的区别在于:函数原型没有函数体部分,且是用分号结束的,就像 变量的声明。 有了函数原型,则只要将其放在对函数的调用之前,则即使函数的定义放在其引用之后, 也不会引起编译失败。这就为进行结构化、模块化程序设计提供了极大的方便。 [例 5-3]使用函数原型。 // Example 5-3:求两数中的大数 #include void main() { int max(int x,int y); cout > a >> b; cout y?x:y; } 分 析:尽管函数 max()的定义出现在对它的调用之后,然而由于使用了函数原型, 程序就能成功地编译通过。函数原型向编译器提供了函数的名字、值的类型和参数的个数及 类型等信息。在函数原型中,参数的名字也可以省略,因此,上例中的函数原型也可以改写 为 int max(int,int); 5.3 函数间的参数传递 在 C++程序中,利用函数不仅可以改善程序的可读性,而且提高了程序的灵活性。由 于函数通常是用于实现一个具体功能的模块,所以它必然要和程序中的其他模块交换信息。 实际上,一个函数可以从函数之外获得一些数据,并可向其调用者返回一些数据。这些数据 主要是通过函数的参数与函数的返回值来传递的。 函数可以没有参数,也可以有一个或多个参数。在参数表中声明的参数(变量)叫做函
第5单元函数 数的形式参数,简称形参。在调用函数时,一般须为每一个形参给出其实际数据,即实际参 数,简称实参。实参可以是变量、常量、表达式,也可以是一个函数调用,但每个实参的数 据类型应该与其所对应的形参的数据类型相匹配 在调用一个带有参数的函数时,就存在一个实参与形参结合方式的问题。在C++中 实参与形参有3种结合方式:值调用、地址调用和引用调用。本单元中仅使用了值调用方式 其余两种结合方式将在以后介绍。 值调用的特点是调用时实参仅将其值赋给了形参,因此,在函数中对形参值的任何修改 都不会影响到实参的值。前面介绍的例子中的函数调用均为值调用。值调用的好处是减少了 调用函数与被调用函数之间的数据依赖,增强了函数自身的独立性。 然而,由于被调用函数向调用函数传递的数据仅有一个返回值,有时显得不够用。在这 种情况下,可以考虑用实参由函数内向调用函数传送信息,这可以通过用函数来修改实参的 值来实现。为此,可使用第6单元中介绍的地址调用和引用调用。 54局部变量和全局变量 根据作用域的不同,可以将C++程序中的变量分为局部变量和全局变量。局部变量是 在函数或分程序中声明的变量,只能在本函数或分程序的范围内使用。而全局变量声明于所 有函数之外,可以为本源程序文件中位于该全局变量声明之后的所有函数共同使用。 全局变量的用途是在各个函数之间建立某种数据传输通道。通常,我们使用返回值和参 数表在函数之间传递数据,这样做的好处是数据流向清晰自然,易于控制。但有时会遇到这 种情况,某个数据为许多函数所共用,且其流向本身就很清晰,为了简化函数的参数表,可 以将其声明为全局变量。例如,在设计图书资料管理系统时,可以将图书卡片数据声明为一 个全局数组,由“购买新书”函数模块将新书书卡加入该数组中,“借阅”、“还书”、“查询” 和“统计”等函数分别参考该数组的内容管理图书资料的流通和利用 初看起来全局变量可以为所有的函数所共用,使用灵活方便,因此颇为一些初学者所喜 爱,在程序中大量使用。实际上,滥用全局变量会破坏程序的模块化结构,使程序难于理解 和调试。因此要尽量少用或不用全局变量。 如果在一段程序中,既有全局变量,也有局部变量,而且全局变量和局部变量的变量名 相同,这时会出现什么情况呢?请看下面的例子 int x //声明全局变量 int func1(intx)//函数 funcl0有一个名为x的参数 int func2 (int y) int x //函数func20中声明了一个名为x的局部变量
第 5 单元 函数 - 91 - 数的形式参数,简称形参。在调用函数时,一般须为每一个形参给出其实际数据,即实际参 数,简称实参。实参可以是变量、常量、表达式,也可以是一个函数调用,但每个实参的数 据类型应该与其所对应的形参的数据类型相匹配。 在调用一个带有参数的函数时,就存在一个实参与形参结合方式的问题。在 C++中, 实参与形参有 3 种结合方式:值调用、地址调用和引用调用。本单元中仅使用了值调用方式, 其余两种结合方式将在以后介绍。 值调用的特点是调用时实参仅将其值赋给了形参,因此,在函数中对形参值的任何修改 都不会影响到实参的值。前面介绍的例子中的函数调用均为值调用。值调用的好处是减少了 调用函数与被调用函数之间的数据依赖,增强了函数自身的独立性。 然而,由于被调用函数向调用函数传递的数据仅有一个返回值,有时显得不够用。在这 种情况下,可以考虑用实参由函数内向调用函数传送信息,这可以通过用函数来修改实参的 值来实现。为此,可使用第 6 单元中介绍的地址调用和引用调用。 5.4 局部变量和全局变量 根据作用域的不同,可以将C++程序中的变量分为局部变量和全局变量。局部变量是 在函数或分程序中声明的变量,只能在本函数或分程序的范围内使用。而全局变量声明于所 有函数之外,可以为本源程序文件中位于该全局变量声明之后的所有函数共同使用。 全局变量的用途是在各个函数之间建立某种数据传输通道。通常,我们使用返回值和参 数表在函数之间传递数据,这样做的好处是数据流向清晰自然,易于控制。但有时会遇到这 种情况,某个数据为许多函数所共用,且其流向本身就很清晰,为了简化函数的参数表,可 以将其声明为全局变量。例如,在设计图书资料管理系统时,可以将图书卡片数据声明为一 个全局数组,由“购买新书”函数模块将新书书卡加入该数组中,“借阅”、“还书”、“查询” 和“统计”等函数分别参考该数组的内容管理图书资料的流通和利用。 初看起来全局变量可以为所有的函数所共用,使用灵活方便,因此颇为一些初学者所喜 爱,在程序中大量使用。实际上,滥用全局变量会破坏程序的模块化结构,使程序难于理解 和调试。因此要尽量少用或不用全局变量。 如果在一段程序中,既有全局变量,也有局部变量,而且全局变量和局部变量的变量名 相同,这时会出现什么情况呢? 请看下面的例子。 int x; // 声明全局变量 int func1(int x) // 函数 func1()有一个名为 x 的参数 { y = x; ... ... } int func2(int y) { int x; // 函数 func2()中声明了一个名为 x 的局部变量
第5单元函数 void maino x=0;//在主函数中为全局变量x赋值 在上面的程序中一共有3个变量x:一个是全局变量,一个是函数 final()的参数, 还有一个是函数func2()中的局部变量。虽然我们说全局变量的作用范围是整个源程序, 但就上面这段程序而言,只有在主函数中才能使用全局变量x,而在其他两个函数中的x 均是它们的参数或局部变量。这种现象可以用“地方保护主义”形象地说明。 也可以在函数内部的变量声明语句之前加上外部说明符 extern说明该函数中用的就是 外部变量,而非一个同名的局部变量。例如 int x =0: //全局变量声明 func o extern int x,y;//变量x,y都是相应的外部变量 与此类似,声明于函数或分程序中的局部变量的作用域是整个函数或分程序,包括其中 嵌套的所有其他分程序。但是,在内层分程序中声明的变量与其外层的函数或分程序中声明 的变量重名时,仍是按上述原则确定各自的作用域,即内层变量声明优先于外层变量声明 不过,为了使程序清晰易读,最好不要在嵌套的分程序中声明重名的变量 自学内容 55内联函数 在调用函数时,系统要做许多工作,主要包括断点现场保护、数据进栈、执行函数体 保存返回值、恢复现场和断点等,开销很大 有些函数的函数体比较简单(如例5-3中的函数max(),在调用时,执行函数体所消 耗的时间与函数调用时的其他时间开销相比就显得微不足道。如果该函数被频繁调用,则附
第 5 单元 函数 - 92 - ... ... } void main() { ... ... x = 0; // 在主函数中为全局变量 x 赋值 ... ... } 在上面的程序中一共有 3 个变量 x:一个是全局变量,一个是函数 func1()的参数, 还有一个是函数 func2()中的局部变量。 虽然我们说全局变量的作用范围是整个源程序, 但就上面这段程序而言,只有在主函数中才能使用全局变量 x,而在其他两个函数中的 x 均是它们的参数或局部变量。这种现象可以用“地方保护主义”形象地说明。 也可以在函数内部的变量声明语句之前加上外部说明符 extern 说明该函数中用的就是 外部变量,而非一个同名的局部变量。例如 int x = 0; // 全局变量声明 ... ... func() { extern int x,y;// 变量 x,y 都是相应的外部变量 ... ... } int y = 0; ... ... 与此类似,声明于函数或分程序中的局部变量的作用域是整个函数或分程序,包括其中 嵌套的所有其他分程序。但是,在内层分程序中声明的变量与其外层的函数或分程序中声明 的变量重名时,仍是按上述原则确定各自的作用域,即内层变量声明优先于外层变量声明。 不过,为了使程序清晰易读,最好不要在嵌套的分程序中声明重名的变量。 自学内容 5.5 内联函数 在调用函数时,系统要做许多工作,主要包括断点现场保护、数据进栈、执行函数体、 保存返回值、恢复现场和断点等,开销很大。 有些函数的函数体比较简单(如例 5-3 中的函数 max()),在调用时,执行函数体所消 耗的时间与函数调用时的其他时间开销相比就显得微不足道。如果该函数被频繁调用,则附
第5单元函数 加的时间开销将大得不容忽视。 C+为了解决这一矛盾,提供了一种称做“内联函数”的机制。该机制通过将函数体的 代码直接插入到函数调用处来节省调用函数的时间开销,这一过程叫做内联函数的扩展。由 于在扩展时对函数的每一次调用均要进行扩展,所以内联函数实际上是一种用空间换时间的 方案 要定义一个内联函数,只需在定义函数时将该函数用关键字 inline修饰即可。 例54将例5-3中的函数max()改写成内联函数 程序 / Example5-4:求两数中的大数 #include inline int max(int x, int y) return x>y?x:y void main o cout >a>> b: cout b?a: b<< endl 使用内联函数时应注意: 1.在C++程序中,除了在函数体中含有循环、 switch分支和复杂嵌套的if语句的函数 外,所有的函数均可以被声明为内联函数。 2.内联函数的定义必须出现在对该函数的调用之前,如例5-4所示。这是因为编译器 在对函数调用语句进行代换时,必须事先知道代换该语句的代码是什么。如果像例5-3那样, 即使在函数原型和函数定义处均加上关键字 inline也不行 3.由于计算机的资源总是有限的,使用内联函数虽然节省了程序运行的时间开销,但 却增大了代码占用内存的空间开销。因此在具体编程时应仔细地权衡时间开销与空间开销之 间的矛盾,以确定是否采用内联函数。 56带有缺省参数的函数 C++允许在函数声明或函数定义中为参数预赋一个或多个缺省值,这样的函数就叫做带 有缺省参数的函数。在调用带有缺省参数的函数时,如果为相应参数指定了参数值,则参数
第 5 单元 函数 - 93 - 加的时间开销将大得不容忽视。 C++为了解决这一矛盾,提供了一种称做“内联函数”的机制。该机制通过将函数体的 代码直接插入到函数调用处来节省调用函数的时间开销,这一过程叫做内联函数的扩展。由 于在扩展时对函数的每一次调用均要进行扩展,所以内联函数实际上是一种用空间换时间的 方案。 要定义一个内联函数,只需在定义函数时将该函数用关键字 inline 修饰即可。 [例 5-4] 将例 5-3 中的函数 max()改写成内联函数。 程 序: // Example 5-4:求两数中的大数 #include inline int max(int x,int y) { return x>y?x:y; } void main() { cout > a >> b; cout b?a:b << endl; 使用内联函数时应注意: 1.在 C++程序中,除了在函数体中含有循环、switch 分支和复杂嵌套的 if 语句的函数 外,所有的函数均可以被声明为内联函数。 2.内联函数的定义必须出现在对该函数的调用之前,如例 5-4 所示。这是因为编译器 在对函数调用语句进行代换时,必须事先知道代换该语句的代码是什么。如果像例 5-3 那样, 即使在函数原型和函数定义处均加上关键字 inline 也不行。 3.由于计算机的资源总是有限的,使用内联函数虽然节省了程序运行的时间开销,但 却增大了代码占用内存的空间开销。因此在具体编程时应仔细地权衡时间开销与空间开销之 间的矛盾,以确定是否采用内联函数。 5.6 带有缺省参数的函数 C++允许在函数声明或函数定义中为参数预赋一个或多个缺省值,这样的函数就叫做带 有缺省参数的函数。在调用带有缺省参数的函数时,如果为相应参数指定了参数值,则参数
第5单元函数 将使用该值;否则参数使用其缺省值。例如,某函数的声明为 double func( double x, double y, int n = 1000) 则其参数n带有缺省参数值。如果以 a= func(b, c) 的方式调用该函数,则参数n取其缺省值1000,而如果以 a func(b, c, 2000) 的方式调用该函数,则参数n的值为2000 使用带有缺省参数的函数时应注意: 1.所有的缺省参数均须放在参数表的最后。如果一个函数有两个以上缺省参数,则在 调用时可省略从后向前的连续若干个参数值。例如对于函数 void func(int x, int nl=l, int n2= 2) 若使用func(5,4);的方式调用该函数,则nl的值为4,n2的值为2 2.缺省参数的声明必须出现在函数调用之前。这就是说,如果存在函数原型,则参数 的缺省值应在函数原型中指定,否则在函数定义中指定。另外,如果函数原型中已 给出了参数的缺省值,则在函数定义中不得重复指定,即使所指定的缺省值完全相 同也不行 57函数重载 在C++的函数库中,有4个功能相似的函数 int double fabs(double) 这些函数的原型均在头文件 math. h中声明,其功能依次为求整型量、双精度实型量和 长整型量的绝对值。同是求某数的绝对值,要用不同的函数实现,不但增加了程序员的记忆 难度,也增加了出错的可能性。能否将求绝对值的方法看成是一个通用的方法,用同一函数 形式调用呢?C++中的函数重载可以满足这一要求。 所谓函数重载,即若干参数和返回值不同的函数共用一个函数名。 例5-5重载绝对值函数 程序: ∥/ Example5-5:重载绝对值函数 #include int abs (int x) return x>0%x: -x
第 5 单元 函数 - 94 - 将使用该值;否则参数使用其缺省值。例如,某函数的声明为: double func(double x,double y,int n = 1000); 则其参数 n 带有缺省参数值。如果以 a = func(b,c) 的方式调用该函数,则参数 n 取其缺省值 1000,而如果以 a = func(b,c,2000); 的方式调用该函数,则参数 n 的值为 2000。 使用带有缺省参数的函数时应注意: 1.所有的缺省参数均须放在参数表的最后。如果一个函数有两个以上缺省参数,则在 调用时可省略从后向前的连续若干个参数值。例如对于函数 void func(int x,int n1 = 1,int n2 = 2); 若使用 func(5,4);的方式调用该函数,则 n1 的值为 4,n2 的值为 2。 2.缺省参数的声明必须出现在函数调用之前。这就是说,如果存在函数原型,则参数 的缺省值应在函数原型中指定,否则在函数定义中指定。另外,如果函数原型中已 给出了参数的缺省值,则在函数定义中不得重复指定,即使所指定的缺省值完全相 同也不行。 5.7 函数重载 在 C++的函数库中,有 4 个功能相似的函数: int abs(int); double fabs(double); 1ong labs(1ong); 这些函数的原型均在头文件 math.h 中声明,其功能依次为求整型量、双精度实型量和 长整型量的绝对值。同是求某数的绝对值,要用不同的函数实现,不但增加了程序员的记忆 难度,也增加了出错的可能性。能否将求绝对值的方法看成是一个通用的方法,用同一函数 形式调用呢?C++中的函数重载可以满足这一要求。 所谓函数重载,即若干参数和返回值不同的函数共用一个函数名。 [例 5-5] 重载绝对值函数。 程 序: // Example 5-5:重载绝对值函数 #include int abs(int x) { return x>0?x:-x; }
第5单元函数 double abs(double x) return x>0?x: -x long abs (long x) return x>0?x: -x void maino int xl=1 double x2=2.5 cout 在第3单元已经介绍了字符串处理类库函数。C+的库函数极多,很难一一列举。本教程的
第 5 单元 函数 - 95 - double abs(double x) { return x>0?x:-x; } 1ong abs(1ong x) { return x>0?x:-x; } void main() { int x1 = 1; double x2 = 2.5; 1ong x3 = 3L; cout 在第 3 单元已经介绍了字符串处理类库函数。C++的库函数极多,很难一一列举。本教程的
第5单元函数 附录4:“常用库函数”给出了部分常用库函数的原型及其说明。学习库函数的用法,最好 的方法是通过联机帮助査看该函数的说明,如有疑问则再编一小验证程序实际测试其参数和 返回值。 59自动变量和静态变量 根据变量的生存期,还可以将变量分为自动变量和静态变量 静态变量的特点是在程序开始运行之前就为其分配了相应的存储空间,在程序的整个运 行期间静态变量一直占用着这些存储空间,直到整个程序运行结束。因此静态变量的生存期 就是整个程序的运行期。所有的全局变量都是静态变量。另外,在主函数的开始声明的局部 变量也具有和整个程序运行期相同的生存期。如果在声明静态变量的同时还说明了初值,则 该初值也是在分配存储的同时设置的,以后在程序的运行期间不再重复设置。 自动变量的特点是在程序运行到自动变量的作用域(即声明了自动变量的那个函数或分 程序)中时才为自动变量分配相应的存储空间,此后才能向变量中存储数据或读取变量中的 数据。一旦退出声明了自动变量的那个函数或分程序之后,程序会立即将自动变量占用的存 储空间释放,被释放的空间还可以重新分配给其他函数中声明的自动变量使用。因此自动变 量的生存期从程序进入声明了该自动变量的函数或分程序开始,到程序退出该函数或分程序 时结束。在此期间之外自动变量是不存在的。自动变量的初值在每次为自动变量分配存储后 都要重新设置。 自动变量对存储空间的利用是动态的,通过分配和回收,不同函数中声明的自动变量可 以在不同的时间中共享同一块存储空间,从而提高了存储器的利用率。显然,前面介绍的局 部变量(也包括函数的参数)都是自动变量。同样显然的是在整个程序运行过程中,一个自 动变量可能经历若干个生存期。而在自动变量的各个不同生存期中程序为该变量分配的存储 空间的具体地址可能并不相同,因此在编写程序时,不能期望在两次调用同一函数时,其中 声明的同一个局部变量的值之间会有什么联系 然而,有时需要在函数中保留一些变量的值,以便下次进入该函数以后仍然可以继续使 用。使用全局变量当然可以达到这一目的,但是使用全局变量会使程序变得难于阅读、难于 调试。此时我们可以将该变量声明为静态局部变量。声明格式是在原来的变量声明语句前面 加上 statIc构成。例如 int funco static int count 0 return ttcount 函数func()中声明了一个静态局部变量 count。静态局部变量同时具有局部变量和自 动变量的特点。静态局部变量 count只能在其定义域(函数func())中使用,但其生存期
第 5 单元 函数 - 96 - 附录 4:“常用库函数”给出了部分常用库函数的原型及其说明。学习库函数的用法,最好 的方法是通过联机帮助查看该函数的说明,如有疑问则再编一小验证程序实际测试其参数和 返回值。 5.9 自动变量和静态变量 根据变量的生存期,还可以将变量分为自动变量和静态变量。 静态变量的特点是在程序开始运行之前就为其分配了相应的存储空间,在程序的整个运 行期间静态变量一直占用着这些存储空间,直到整个程序运行结束。因此静态变量的生存期 就是整个程序的运行期。所有的全局变量都是静态变量。另外,在主函数的开始声明的局部 变量也具有和整个程序运行期相同的生存期。如果在声明静态变量的同时还说明了初值,则 该初值也是在分配存储的同时设置的,以后在程序的运行期间不再重复设置。 自动变量的特点是在程序运行到自动变量的作用域(即声明了自动变量的那个函数或分 程序)中时才为自动变量分配相应的存储空间,此后才能向变量中存储数据或读取变量中的 数据。一旦退出声明了自动变量的那个函数或分程序之后,程序会立即将自动变量占用的存 储空间释放,被释放的空间还可以重新分配给其他函数中声明的自动变量使用。因此自动变 量的生存期从程序进入声明了该自动变量的函数或分程序开始,到程序退出该函数或分程序 时结束。在此期间之外自动变量是不存在的。自动变量的初值在每次为自动变量分配存储后 都要重新设置。 自动变量对存储空间的利用是动态的,通过分配和回收,不同函数中声明的自动变量可 以在不同的时间中共享同一块存储空间,从而提高了存储器的利用率。显然,前面介绍的局 部变量(也包括函数的参数)都是自动变量。同样显然的是在整个程序运行过程中,一个自 动变量可能经历若干个生存期。而在自动变量的各个不同生存期中程序为该变量分配的存储 空间的具体地址可能并不相同,因此在编写程序时,不能期望在两次调用同一函数时,其中 声明的同一个局部变量的值之间会有什么联系。 然而,有时需要在函数中保留一些变量的值,以便下次进入该函数以后仍然可以继续使 用。使用全局变量当然可以达到这一目的,但是使用全局变量会使程序变得难于阅读、难于 调试。此时我们可以将该变量声明为静态局部变量。声明格式是在原来的变量声明语句前面 加上 static 构成。例如 int func() { static int count = 0; ... ... return ++count; } 函数 func()中声明了一个静态局部变量 count。 静态局部变量同时具有局部变量和自 动变量的特点。静态局部变量 count 只能在其定义域(函数 func())中使用,但其生存期