收藏 分享(赏)

C++实用教程[郑阿奇主编]5.ppt

上传人:精品资料 文档编号:10809463 上传时间:2020-01-10 格式:PPT 页数:38 大小:668.50KB
下载 相关 举报
C++实用教程[郑阿奇主编]5.ppt_第1页
第1页 / 共38页
C++实用教程[郑阿奇主编]5.ppt_第2页
第2页 / 共38页
C++实用教程[郑阿奇主编]5.ppt_第3页
第3页 / 共38页
C++实用教程[郑阿奇主编]5.ppt_第4页
第4页 / 共38页
C++实用教程[郑阿奇主编]5.ppt_第5页
第5页 / 共38页
点击查看更多>>
资源描述

1、第5章 函 数,5.1 函数概述,主函数main不仅是程序的入口函数,而且与其他函数相比较还有许多使用上的限制。例如,它不能被其他函数调用,不能用inline和static来说明等。ANSI/ISO C+还规定主函数main的函数类型必须是int,以保证程序的可移植性。 图5.1 函数调用关系示意图 自定义函数是用户根据程序的需要,将某个功能相对独立的程序定义成一个函数,或将解决某个问题的算法用一个函数来组织。,5.1 函数调用关系示意图,5.2 函数的定义和声明,与变量的使用规则相同,在C+程序中函数定要先定义后调用,5.2.1 函数的定义,C+中每一个函数的定义都由4部分组成,即函数名、函

2、数类型、形式参数表和函数体,其定义格式如下: ( ) 函数体 注意:函数头末尾没有也不能有分号“;”,函数体花括号“”后面也没有分号“;” .,例:定义一个函数sum,下面就函数定义的几个部分分别说明,函数类型和返回值:若函数类型为void,则表示该函数没有返回值。 2.函数名:注意自定义函数名与库函数名或系统命名尽量不要相同。 3. 形式参数表: 在函数定义中,当形参个数为0时,应在圆括号中使用void关键字,表示没有形参,这是一个良好的编程习惯。如int f( void ) .。,4. 形参的作用和设计:函数中的形参,用来指定调用此函数时所需要的 参数个数和类型。一个函数的函数体中必须有相

3、关 语句对形参进行操作,否则形参定义毫无意义。 5. 函数体: 函数体由一对花括号中的若干语句组成,用于实现这个函数的功能。它仅为程序结构而设定,本身不实现任何操作 。注意:C+不允许在一个函数体中再定义函数,5.2.2 函数的调用和声明,1. 函数的实参和形参要注意形参和实参有如下区别 2. 函数的调用函数调用的一般格式如下:( )注:调用函数时要注意,实参与形参的个数应相等,类型应一致,且按顺序对应,一一传递数据,3. 函数的声明 声明一个函数按下列格式进行:( )下面几种形式都是对sum函数原型的合法声明: int sum(int a, int b); / 允许原型声明时的形参名与 定义

4、时不同 int sum(int, int); / 省略全部形参名 int sum(int a, int); / 省略部分形参名 int sum(int, int b); / 省略部分形参名,5.3 函数的参数特性,5.3.1 全局变量和局部变量 C+中每一个变量必须先定义后使用,若变量是在函数头或函数体内定义的,则该变量就是一个局部变量,它只能在函数体内使用,函数体外则不能使用。 若变量是在函数外部(如在main主函数前)定义的,则它从定义开始一直到源文件结束都能被后面的所有函数或语句引用,这样的变量称为全局变量。,#include using namespace std;函数声明和定义 vo

5、id f( int n); / 函数原型声明 int a = 8; / 定义全局变量 int main() coutaendl; / 输出8f(10);coutaendl; / 输出10 return 0; void f(int n ) / 函数定义 a = n; / 将全局变量的值改为n 代码中,变量a是在main函数中定义的,是一个全局变量,因此它能在后面的main函数和f函数中使 用。程序运行结果如下: 810,例Ex_Global 使用全局变量示例,5.3.2 函数调用的内部机制,函数调用是在栈内存空间中完成的,栈的工作原理是先入后出。当调用一个函数时,整个调用过程分为三步进行:调用初

6、始化、执行函数调用、调用后处理 。,从图中可以看出,在调用函数sum时,C+首先进行如下初始化步骤。, 因sum函数调用需要指定实参,这里是10和20,故先从右向左将实参值20(14h)和10(0Ah)分别入栈(在汇编中使用push指令来进行)。 把函数返回后执行的第一条指令地址入栈(在汇编中call指令隐含一个这样的操作,此时入栈的地址是004015C1)。 将控制权转交到被调函数处(通过call和jmp指令来进行),然后执行函数调用,其步骤如下所述。 将所有相关寄存器的运行状态入栈,即保护现场。, 在栈中开辟并预留一定数量的临时内存空间,此时函数的 形参和函数体中定义的变量的内存空间的分配

7、等 操作一并在 此步完成,其中形参的内存空间就是已压入栈中的实参值所 在的内存空间。, 执行函数体中的其他语句(z = x+y; return z;)。 函数在返回时(这里是在“return z;”执行之后),进行如下函数调用后处理的步骤。 将返回值保存在栈中的临时内存空间中(如果无返回值,则此步不执行)。 将所有入栈的相关寄存器运行状态出栈(使用pop指令),即恢复现场。 释放栈空间,根据返回地址,回到主调函数,执行下一条指令(即地址004015C1中的指令add esp,8)。,5.3.3 参数传递方式,函数的参数传递有两种方式:一是按值传递,二是地址传递或引用传递。 值传递具有如下特点

8、:1)在值传递方式下,若实参指定的是变量,则在调用初始化时入栈的是实参变量的值而不是实参变量的地址 2)即使形参的值在函数中发生了变化,函数调用结束后,实参的值不会受到影响。,例Ex_SwapValue 交换函数两个参数的值,#include using namespace std; void swap(float x, float y); / 函数原型说明 int main() float a = 20, b = 40;cout“a = “a“, b = “b“n“;swap(a, b); / 函数调用cout“a = “a“, b = “b“n“;return 0; void swap(f

9、loat x, float y) / 函数定义 float temp;temp = x; x = y; y = temp;cout“x = “x“, y = “y“n“; ,程序运行结果如下:,a=20, b=40 X=40, y=20 a=20, b=40注:函数值传递方式的最大好处是保持函数的独立性。该方式下,函数只有通过指定函数类型并在函数体中使用return来返回某一类型的数值。,5.3.4 函数的默认形参值,在C+中,允许在函数声明或定义时给一个或多个参数指定默认值。这样在调用时,可以不给出实际参数,而按指定的默认值工作 void delay(int loops = 1000) /

10、函数定义,1000为形参loops的默认值 if ( 0 = loops) return;for (int i=0; iloops; i+); / 空循环,起延时作用 这样,当有调用 delay(); / 和delay(1000)等效 时,程序就会自动将loops当1000的默认值来处理。当然,也可在函数调用时指定相应的实际参数值,例如: delay(2000); / 形参loops的值为2000,在设置函数的默认形参值时要注意以下5点:,(1)当函数既有原型声明又有定义时,默认参数只能在原型声明中指定,而不能在函数定义中指定。 (2)当一个函数需要有多个默认参数时,则在形参分布中,默认参数应

11、严格从右到左逐次定义和指定,中间不能跳开。 (3)当带有默认参数的函数调用时,系统按从左到右的顺序将实参与形参结合。当实参的数目不足时,系统将按同样的顺序用声明或定义中的默认值来补齐所缺少的参数。 (4)由于对同一个函数的原型可进行多次声明,因此在函数声明中指定多个默认参数时,可用多条函数原型声明语句来指定,但同一个参数的默认值只能指定一次 (5)默认参数值可以是全局变量、全局常量,甚至是一个函数,但不能是局部变量。因为默认参数的函数调用是在编译时确定的,而局部变量的值在编译时无法确定。,5.4 函数的调用特性,在C+中,函数还有重载、内联调用、嵌套调用及递归调用等特性,相应的函数被称为重载函

12、数、内联函数、嵌套函数和递归函数。 5.4.1 函数重载 函数重载是指C+允许多个同名的函数存在,但同名的各个函数的形参必须有区别:要么形参的个数不同;要么形参的个数相同,但参数类型不同,例Ex_OverLoad 编程求两个或三个操作数之和。,#include using namespace std; int sum(int x, int y); int sum(int x, int y, int z); double sum(double x, double y); double sum(double x, double y, double z); int main() coutsum(2,

13、 5)endl; / 结果为7coutsum(2, 5, 7)endl; / 结果为14coutsum(1.2, 5.0, 7.5)endl; / 结果为13.7 return 0; , int sum(int x, int y) return x+y; int sum(int x, int y, int z) return x+y+z; double sum(double x, double y) return x+y; double sum(double x, double y, double z) return x+y+z; 程序运行结果如下:71413.7,5.4.2 内联函数,例Ex

14、_Inline 用内联函数实现求两个实数的最大值。 #include using namespace std; inline float fmax(float x, float y) return xy?x:y; int main() float a;a = fmax(5, 10); / Acout10 ? 5 : 10;,程序运行结果如下:,最大的数为:10要注意使用内联函数的一些限制: (1)内联函数中不能有数组定义,也不能有任何静态类型的定义。 (2)内联函数中不能含有循环、switch和复杂嵌套的if语句。 (3)内联函数不能是递归函数。总之,内联函数一般是比较小的、经常被调用的、大多

15、可在一行写完的函数,并常用来代替以后要讨论的带参数的宏定义,5.4.3 函数嵌套调用,C+允许在函数中再调用其他函数,这种调用称为函数的嵌套调用。 例Ex_Root 用函数嵌套调用求解一元二次方程的根 : #include #include using namespace std; void print( double r1, double r2, int n = 2); / 输出根,默认时根的个数n为2 double sdelta( double a, double b, double c); / 计算b*b-4*a*c的平方根 void root( double a, double b,

16、double c); / 计算并输出根,int main() double a = 2.0, b = 6.0, c = 3.0; / 定义并初始化变量root(a, b, c); / 调用函数return 0; void print( double r1, double r2, int n) if (n1) cout“方程无根!“;else cout“方程有 “n“ 根:n“;cout“t根1: “r1;if (n=2) cout“t根2: “r2;coutendl; double sdelta( double a, double b, double c),double res;double

17、d = b*b - 4.0 * a * c;/ 当d小于0无平方根,返回值为-1,若小于1.0e-10,则d认为是0,返回值为0/ 否则调用sqrt库函数求平方根if (d0.0) res = -1.0;else if ( fabs(d) 1.0e-10 ) res = 0.0; else res = sqrt(d);return res; void root( double a, double b, double c) double d = sdelta(a, b, c); / 调用函数sdelta,此时形参作为函数sdelta的实参if (d0.0) print(0.0, 0.0, 0);

18、 / 方程无根,输入结果else if ( 0.0 = a),/ 如果a值为0,则无论d是何值,根都是-c/b;如果b值还为0,则无根if ( 0.0 = b ) print(0.0, 0.0, 0);else print(-c/b, 0.0, 1);else if ( 0.0 = d) / 方程有相同的两个根,即一个根print( -b /(2.0 * a), 0.0, 1);else / 方程有两个根print( (-b + d)/(2.0 * a), (-b - d)/(2.0 * a); 程序运行结果如下:,5.4.4 递归函数,C+允许在调用一个函数的过程中直接或间接地调用函数本身,

19、这称为函数的递归调用。递归(Recursion)是一种常用的程序方法(算法),相应的函数称为递归函数。 1. 递归函数的实现 例如,用递归函数编程求n!=n*(n-1)*(n-2)*2*1。它也可用下式表示:1 n=0 n= n*(n-1)! n0 由于n!和(n-1)!都是同一个问题的求解,因此可将n!用递归函数long factorial(int n)来描述,程序代码如下。,例Ex_Factorial 编程求n的阶乘n!,#include using namespace std; long factorial(int n); int main() coutfactorial(4)endl;

20、 / 结果为24return 0; long factorial(int n) long result = 0;if (0 = n) result = 1;else result = n*factorial(n-1); / 进行自身调用return result; 程序运行结果如下: 24,2. 递归函数的运行过程,int m = 0; / 全局变量 long factorial(int n) long result = 0;int i;for (i=0; im; i+) cout“t“;/ 按调用的次数来决定输出位置couthex ,程序运行结果如下:,可见,尽管函数factorial递归调

21、用时的形参都是n,但每次调用都会为其分配不同的内存空间,且每次调用都是按照调用初始化、执行函数代码、调用后处理这三步进行。因此,递归函数实际上可理解为同名函数的多级嵌套调用,3. 递归的条件,一般来说,递归需要满足两个条件:一是要有递归公式,即能将一个问题可化解一个或多个子问题求解,且子问题和原问题具有相同的解法 若将例Ex_Factorial中的函数factorial代码修改成下列代码: long factorial(int n) return n*factorial(n-1); 则因无终止条件而使递归无休止地进行下去。若修改成 long factorial(int n) long resu

22、lt = 0;if (0 = n) result = 1;else result = n*factorial(n);return result; 则递归调用时,由于参数n没有变化,因此代码中的if条件永远不会满足,递归会一直进行下去,直到栈内存空间用完为止。,5.5 递归程序设计,5.5.1 设计步骤 一般地,用递归方法进行“问题求解”时,需要依次进行下列三个步骤 化解问题,求得算法 规划递归路线 确定形参,设计递归终止条件,5.5.2 Fibonacci数列,1. Fibonacci数列问题例Ex_Fibonacci1 编程求Fibonacci数列的第n项 #include using na

23、mespace std; long fib(int n); int main() coutfib(8) “n“; / 结果为21return 0; long fib(int n) switch(n) case 0: return 0;case 1:case 2: return 1;return fib(n-1)+fib(n-2); 程序运行结果如下:21,5.5.3 Hanoi塔问题,例Ex_Hanoi 求解Hanoi塔问题。 #include using namespace std; void move( int n, char s, char d) coutn“, “s“dendl; void hanoi( int n, char A = A, char B = B, char C = C) if (1 = n) move(n, A, C);elsehanoi( n-1, A, C, B); move(n, A, C); hanoi( n-1, B, A, C); int main() hanoi(3); return 0; ,程序运行结果如下:,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报