第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++函数的参数声明格式为: <类型><参数1>,<类型>参数 <类型><参数n> 例如 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 <iostream. h> //函数facO:计算阶乘 long fac(int n return lse if (n==o) return IL while(n>1)
第 5 单元 函数 - 88 - 型应与函数声明中的函数值类型一致,否则可能出现错误。 有些函数可能没有函数值,或者说其函数值对调用者来说是不重要的。这时调用该函数 实际上是为了得到运行该函数内部的程序段的其他效果。这一点与数学中的函数概念有所不 同,需特别注意。如果要声明一个函数确实没有返回值,可以使用声明符 void。例如主函数 void main() { … … } 既没有返回值,也不需要参数。对于一个被声明为 void 类型的函数,编译程序如果发现在 程序中用到了其返回值,或者在该函数中出现了有返回值的 return 语句,都会报告相应的错 误信息,便于检查程序是否有错。 2.参数声明:C++函数的参数声明格式为: <类型><参数 1>,<类型><参数 2>,…,<类型><参数 n> 例如 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 <iostream.h> // 函数 fac():计算阶乘 long fac(int n) { long result = 1L; if(n<0) return -1L; else if(n==0) return 1L; while (n>1) {
第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 << "Please input a number n to calculte n! :"; cin >> 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 <iostream. h> void maino int max(int x, int y) cout < Enter two integer: int a, b cin >> a>b cout < "The maxium number is"<< max(a, b)<< endl int max(int x, int y) return x>y?X: y 分析:尽管函数max()的定义出现在对它的调用之后,然而由于使用了函数原型 程序就能成功地编译通过。函数原型向编译器提供了函数的名字、值的类型和参数的个数及 类型等信息。在函数原型中,参数的名字也可以省略,因此,上例中的函数原型也可以改写 nt max (int, int ): 53函数间的参数传递 在C艹程序中,利用函数不仅可以改善程序的可读性,而且提高了程序的灵活性。由 于函数通常是用于实现一个具体功能的模块,所以它必然要和程序中的其他模块交换信息。 实际上,一个函数可以从函数之外获得一些数据,并可向其调用者返回一些数据。这些数据 主要是通过函数的参数与函数的返回值来传递的 函数可以没有参数,也可以有一个或多个参数。在参数表中声明的参数(变量)叫做函
第 5 单元 函数 - 90 - 的声明。因此,在前面各单元的例子中,函数的定义均放在程序的前部。另外,在 C++中还 有一种函数的引用性声明,即函数原型(Function Prototype),通常也称其为函数声明。函 数原型的一般形式为 <函数返回值的类型声明> <函数名> (<参数表>); 其中各部分的意义与函数定义相同。 函数原型与函数定义的区别在于:函数原型没有函数体部分,且是用分号结束的,就像 变量的声明。 有了函数原型,则只要将其放在对函数的调用之前,则即使函数的定义放在其引用之后, 也不会引起编译失败。这就为进行结构化、模块化程序设计提供了极大的方便。 [例 5-3]使用函数原型。 // Example 5-3:求两数中的大数 #include <iostream.h> void main() { int max(int x,int y); cout << "Enter two integer:"; int a,b; cin >> a >> b; cout << " The maxium number is" << max(a,b) << endl; } int max(int x,int y) { return x>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 的局部变量