《C++实用教程[郑阿奇主编]5.pptx》由会员分享,可在线阅读,更多相关《C++实用教程[郑阿奇主编]5.pptx(38页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、5.1 函数概述主函数main不仅是程序的入口函数,而且与其他函数相比较还有许多使用上的限制。例如,它不能被其他函数调用,不能用inline和static来说明等。ANSI/ISO C+还规定主函数main的函数类型必须是int,以保证程序的可移植性。图5.1函数调用关系示意图自定义函数是用户根据程序的需要,将某个功能相对独立的程序定义成一个函数,或将解决某个问题的算法用一个函数来组织。第1页/共38页5.1 函数调用关系示意图第2页/共38页5.2 函数的定义和声明与变量的使用规则相同,在C+程序中函数定要先定义后调用第3页/共38页5.2.1 函数的定义C+中每一个函数的定义都由4部分组成
2、,即函数名、函数类型、形式参数表和函数体,其定义格式如下:()函数体注意:函数头末尾没有也不能有分号“;”,函数体花括号“”后面也没有分号“;”.第4页/共38页例:定义一个函数sum 第5页/共38页下面就函数定义的几个部分分别说明1.函数类型和返回值:若函数类型为void,则表示该函数没有返回值。2.函数名:注意自定义函数名与库函数名或系统命名尽量不要相同。3.形式参数表:在函数定义中,当形参个数为0时,应在圆括号中使用void关键字,表示没有形参,这是一个良好的编程习惯。如intf(void).。第6页/共38页4.形参的作用和设计:函数中的形参,用来指定调用此函数时所需要的参数个数和类
3、型。一个函数的函数体中必须有相关语句对形参进行操作,否则形参定义毫无意义。5.函数体:函数体由一对花括号中的若干语句组成,用于实现这个函数的功能。它仅为程序结构而设定,本身不实现任何操作。注意:C+不允许在一个函数体中再定义函数第7页/共38页5.2.2 函数的调用和声明1.函数的实参和形参要注意形参和实参有如下区别2.函数的调用函数调用的一般格式如下:()注:调用函数时要注意,实参与形参的个数应相等,类型应一致,且按顺序对应,一一传递数据第8页/共38页3.函数的声明声明一个函数按下列格式进行:()下面几种形式都是对sum函数原型的合法声明:int sum(int a,int b);/允许原
4、型声明时的形参名与 定义时不同int sum(int,int);/省略全部形参名int sum(int a,int);/省略部分形参名int sum(int,int b);/省略部分形参名第9页/共38页5.3 函数的参数特性5.3.1全局变量和局部变量C+中每一个变量必须先定义后使用,若变量是在函数头或函数体内定义的,则该变量就是一个局部变量,它只能在函数体内使用,函数体外则不能使用。若变量是在函数外部(如在main主函数前)定义的,则它从定义开始一直到源文件结束都能被后面的所有函数或语句引用,这样的变量称为全局变量。第10页/共38页#includeusingnamespacestd;函数
5、声明和定义voidf(intn);/函数原型声明inta=8;/定义全局变量intmain()coutaendl;/输出8f(10);coutaendl;/输出10return0;voidf(intn)/函数定义a=n;/将全局变量的值改为n代码中,变量a是在main函数中定义的,是一个全局变量,因此它能在后面的main函数和f函数中使用。程序运行结果如下:810例Ex_Global 使用全局变量示例 第11页/共38页5.3.2 函数调用的内部机制函数调用是在栈内存空间中完成的,栈的工作原理是先入后出。当调用一个函数时,整个调用过程分为三步进行:调用初始化、执行函数调用、调用后处理。第12页
6、/共38页从图中可以看出,在调用函数sum时,C+首先进行如下初始化步骤。因sum函数调用需要指定实参,这里是10和20,故先从右向左将实参值20(14h)和10(0Ah)分别入栈(在汇编中使用push指令来进行)。把函数返回后执行的第一条指令地址入栈(在汇编中call指令隐含一个这样的操作,此时入栈的地址是004015C1)。将控制权转交到被调函数处(通过call和jmp指令来进行),然后执行函数调用,其步骤如下所述。将所有相关寄存器的运行状态入栈,即保护现场。第13页/共38页 在栈中开辟并预留一定数量的临时内存空间,此时函数的 形参和函数体中定义的变量的内存空间的分配等 操作一并在 此步
7、完成,其中形参的内存空间就是已压入栈中的实参值所 在的内存空间。执行函数体中的其他语句(z=x+y;returnz;)。函数在返回时(这里是在“returnz;”执行之后),进行如下函数调用后处理的步骤。将返回值保存在栈中的临时内存空间中(如果无返回值,则此步不执行)。将所有入栈的相关寄存器运行状态出栈(使用pop指令),即恢复现场。释放栈空间,根据返回地址,回到主调函数,执行下一条指令(即地址004015C1中的指令addesp,8)。第14页/共38页5.3.3 参数传递方式函数的参数传递有两种方式:一是按值传递,二是地址传递或引用传递。值传递具有如下特点:1)在值传递方式下,若实参指定的
8、是变量,则在调用初始化时入栈的是实参变量的值而不是实参变量的地址2)即使形参的值在函数中发生了变化,函数调用结束后,实参的值不会受到影响。第15页/共38页例Ex_SwapValue 交换函数两个参数的值#includeusingnamespacestd;voidswap(floatx,floaty);/函数原型说明intmain()floata=20,b=40;couta=a,b=bn;swap(a,b);/函数调用couta=a,b=bn;return0;voidswap(floatx,floaty)/函数定义floattemp;temp=x;x=y;y=temp;coutx=x,y=yn
9、;第16页/共38页程序运行结果如下:a=20,b=40X=40,y=20a=20,b=40注:函数值传递方式的最大好处是保持函数的独立性。该方式下,函数只有通过指定函数类型并在函数体中使用return来返回某一类型的数值。第17页/共38页5.3.4 函数的默认形参值在C+中,允许在函数声明或定义时给一个或多个参数指定默认值。这样在调用时,可以不给出实际参数,而按指定的默认值工作voiddelay(intloops=1000)/函数定义,1000为形参loops的默认值if(0=loops)return;for(inti=0;iloops;i+);/空循环,起延时作用这样,当有调用delay
10、();/和delay(1000)等效时,程序就会自动将loops当1000的默认值来处理。当然,也可在函数调用时指定相应的实际参数值,例如:delay(2000);/形参loops的值为2000第18页/共38页在设置函数的默认形参值时要注意以下5点:(1)当函数既有原型声明又有定义时,默认参数只能在原型声明中指定,而不能在函数定义中指定。(2)当一个函数需要有多个默认参数时,则在形参分布中,默认参数应严格从右到左逐次定义和指定,中间不能跳开。(3)当带有默认参数的函数调用时,系统按从左到右的顺序将实参与形参结合。当实参的数目不足时,系统将按同样的顺序用声明或定义中的默认值来补齐所缺少的参数。
11、(4)由于对同一个函数的原型可进行多次声明,因此在函数声明中指定多个默认参数时,可用多条函数原型声明语句来指定,但同一个参数的默认值只能指定一次(5)默认参数值可以是全局变量、全局常量,甚至是一个函数,但不能是局部变量。因为默认参数的函数调用是在编译时确定的,而局部变量的值在编译时无法确定。第19页/共38页5.4 函数的调用特性在C+中,函数还有重载、内联调用、嵌套调用及递归调用等特性,相应的函数被称为重载函数、内联函数、嵌套函数和递归函数。5.4.1函数重载函数重载是指C+允许多个同名的函数存在,但同名的各个函数的形参必须有区别:要么形参的个数不同;要么形参的个数相同,但参数类型不同第20
12、页/共38页例Ex_OverLoad 编程求两个或三个操作数之和。#includeusingnamespacestd;intsum(intx,inty);intsum(intx,inty,intz);doublesum(doublex,doubley);doublesum(doublex,doubley,doublez);intmain()coutsum(2,5)endl;/结果为7coutsum(2,5,7)endl;/结果为14coutsum(1.2,5.0,7.5)endl;/结果为13.7return0;第21页/共38页int sum(int x,int y)return x+y;i
13、nt 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第22页/共38页5.4.2 内联函数例Ex_Inline 用内联函数实现求两个实数的最大值。#includeusingnamespacestd;inlinefloatfmax(floatx,floaty)returnxy?x:y;intmain()floata;a=fmax(5,10);/Acout最大的数
14、为:a10?5:10;第23页/共38页程序运行结果如下:最大的数为:10要注意使用内联函数的一些限制:(1)内联函数中不能有数组定义,也不能有任何静态类型的定义。(2)内联函数中不能含有循环、switch和复杂嵌套的if语句。(3)内联函数不能是递归函数。总之,内联函数一般是比较小的、经常被调用的、大多可在一行写完的函数,并常用来代替以后要讨论的带参数的宏定义第24页/共38页5.4.3 函数嵌套调用C+允许在函数中再调用其他函数,这种调用称为函数的嵌套调用。例Ex_Root 用函数嵌套调用求解一元二次方程的根:#include#includeusingnamespacestd;voidpr
15、int(doubler1,doubler2,intn=2);/输出根,默认时根的个数n为2doublesdelta(doublea,doubleb,doublec);/计算b*b-4*a*c的平方根voidroot(doublea,doubleb,doublec);/计算并输出根第25页/共38页intmain()doublea=2.0,b=6.0,c=3.0;/定义并初始化变量root(a,b,c);/调用函数return0;voidprint(doubler1,doubler2,intn)if(n1)cout方程无根!;elsecout方程有n根:n;coutt根1:r1;if(n=2)c
16、outt根2:r2;coutendl;doublesdelta(doublea,doubleb,doublec)第26页/共38页doubleres;doubled=b*b-4.0*a*c;/当d小于0无平方根,返回值为-1,若小于1.0e-10,则d认为是0,返回值为0/否则调用sqrt库函数求平方根if(d0.0)res=-1.0;elseif(fabs(d)1.0e-10)res=0.0;elseres=sqrt(d);returnres;voidroot(doublea,doubleb,doublec)doubled=sdelta(a,b,c);/调用函数sdelta,此时形参作为函数
17、sdelta的实参if(d0由于n!和(n-1)!都是同一个问题的求解,因此可将n!用递归函数longfactorial(intn)来描述,程序代码如下。第29页/共38页例Ex_Factorial 编程求n的阶乘n!#includeusingnamespacestd;longfactorial(intn);intmain()coutfactorial(4)endl;/结果为24return0;longfactorial(intn)longresult=0;if(0=n)result=1;elseresult=n*factorial(n-1);/进行自身调用returnresult;程序运行结
18、果如下:24第30页/共38页2.递归函数的运行过程int m=0;/全局变量long factorial(int n)long result=0;int i;for(i=0;im;i+)coutt;/按调用的次数来决定输出位置couthex&n:decn,result=resultendl;/输出形参n的地址和值以及result的值m+;if(0=n)result=1;elseresult=n*factorial(n-1);m-;for(i=0;im;i+)coutt;couthex&n:decn,result=resultendl;/输出形参n的地址和值以及result的值return r
19、esult;第31页/共38页程序运行结果如下:可见,尽管函数factorial递归调用时的形参都是n,但每次调用都会为其分配不同的内存空间,且每次调用都是按照调用初始化、执行函数代码、调用后处理这三步进行。因此,递归函数实际上可理解为同名函数的多级嵌套调用 第32页/共38页3.递归的条件一般来说,递归需要满足两个条件:一是要有递归公式,即能将一个问题可化解一个或多个子问题求解,且子问题和原问题具有相同的解法若将例Ex_Factorial中的函数factorial代码修改成下列代码:longfactorial(intn)returnn*factorial(n-1);则因无终止条件而使递归无休
20、止地进行下去。若修改成longfactorial(intn)longresult=0;if(0=n)result=1;elseresult=n*factorial(n);returnresult;则递归调用时,由于参数n没有变化,因此代码中的if条件永远不会满足,递归会一直进行下去,直到栈内存空间用完为止。第33页/共38页5.5 递归程序设计5.5.1设计步骤一般地,用递归方法进行“问题求解”时,需要依次进行下列三个步骤1.化解问题,求得算法2.规划递归路线3.确定形参,设计递归终止条件第34页/共38页5.5.2 Fibonacci数列1.Fibonacci数列问题例Ex_Fibonacc
21、i1 编程求Fibonacci数列的第n项#includeusingnamespacestd;longfib(intn);intmain()coutfib(8)n;/结果为21return0;longfib(intn)switch(n)case0:return0;case1:case2:return1;returnfib(n-1)+fib(n-2);程序运行结果如下:21第35页/共38页5.5.3 Hanoi塔问题例Ex_Hanoi 求解Hanoi塔问题。#includeusingnamespacestd;voidmove(intn,chars,chard)coutn,sdendl;voidhanoi(intn,charA=A,charB=B,charC=C)if(1=n)move(n,A,C);elsehanoi(n-1,A,C,B);move(n,A,C);hanoi(n-1,B,A,C);intmain()hanoi(3);return0;第36页/共38页程序运行结果如下:第37页/共38页谢谢您的观看!第38页/共38页
限制150内