资源预览内容
第1页 / 共64页
第2页 / 共64页
第3页 / 共64页
第4页 / 共64页
第5页 / 共64页
第6页 / 共64页
第7页 / 共64页
第8页 / 共64页
第9页 / 共64页
第10页 / 共64页
亲,该文档总共64页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
第第5 5章章 函数与运算符的重载函数与运算符的重载5.1 5.1 三次方程求根程序的设计三次方程求根程序的设计5.2 5.2 函数的说明与使用函数的说明与使用5.3 5.3 函数的嵌套与递归函数的嵌套与递归5.4 5.4 函数与运算符的重载函数与运算符的重载5.5 5.5 函数与函数与C+C+程序结构程序结构5.6 5.6 程序实例程序实例15.1 5.1 三次方程求根程序的设计三次方程求根程序的设计 - -计算三次方程计算三次方程x x3 3+px+q=0+px+q=0的一个实根的公式为的一个实根的公式为x xr r= =n为了从系数为了从系数p p、q q 计算实根计算实根x xr r, ,把公式的计算分解为下面几把公式的计算分解为下面几步:步: 1 1)令实数)令实数x xr rA+B ;A+B ; 2 2)令实数)令实数A, B A, B 分别为实数分别为实数 R,S R,S 的立方根:的立方根: 3 3)令)令 R = -q/2 + a, S = -q/2R = -q/2 + a, S = -q/2a ; (5.2)a ; (5.2) 4 4)令)令 a = a = sqrtsqrt (q/2) *(q/2) + (q/3) * (q/3) * (q/3) ; (q/2) *(q/2) + (q/3) * (q/3) * (q/3) ; 实际的计算过程为:实际的计算过程为: 用用(4)(4)计算得到计算得到a ; a ; 用用(3)(3)计算得到计算得到 R R 和和S S ;求出;求出 R R 和和 S S 的立方根的立方根 A A 和和 B ;B ;最后得到实根最后得到实根x xr r。 2 5.1 5.1 三次方程求根程序的设计三次方程求根程序的设计计算立方根的迭代公式计算立方根的迭代公式 Float Float cuberootcuberoot(float xfloat x)/精确到小数点后精确到小数点后6 6位位 float root , float root , crootcroot; const float const float epseps1e1e6 6; crootcrootx x;dodo rootrootcrootcroot; crootcroot(2*root2*rootx/x/(root*root*rootroot)/3/3; whilewhile(fabsfabs(crootcrootrootroot)epseps);); returnreturn(crootcroot););3 5.1 5.1 三次方程求根程序的设计三次方程求根程序的设计# include# includeiostream.hiostream.h /program5-1/program5-1# include# includemath.hmath.h float float cuberootcuberoot(floatfloat);); void mainvoid main(voidvoid) float p, q, float p, q, xrxr; coutcoutInput parameters Input parameters p,qp,q: : ; cincinp pq; q; float afloat asqrtsqrt (q/2) *(q/2) + (q/3) * (q/3) * (q/3) ; (q/2) *(q/2) + (q/3) * (q/3) * (q/3) ; xrxrcuberootcuberoot(q q2 2a a)cuberootcuberoot(q q2 2a a);); coutcoutendlendlThe real root of the equation isThe real root of the equation isxrxr;float float cuberootcuberoot(float xfloat x) 4 5.1 5.1 三次方程求根程序的设计三次方程求根程序的设计 课本课本p129p129显示了不使用函数的程序。其中显示了不使用函数的程序。其中crootcroot 的立方根的运算的立方根的运算进行了两次进行了两次, ,所以该计算程序要重复两次所以该计算程序要重复两次, , 当程序较长当程序较长, ,或计算次或计算次数更多时,采用数更多时,采用“子程序子程序”的方案可以大大缩短程序的长度。的方案可以大大缩短程序的长度。 特别是当程序比较复杂时,可以使得程序显得清晰,在特别是当程序比较复杂时,可以使得程序显得清晰,在program5_1 program5_1 中,中,mainmain()中不涉及计算立方根的细节,显得简()中不涉及计算立方根的细节,显得简洁,而在洁,而在cuberootcuberoot()中只解决一个浮点数的立方根的计算,也()中只解决一个浮点数的立方根的计算,也很清楚。很清楚。 另外,还可以把立方根的计算与另外,还可以把立方根的计算与C+C+语言中的运算符和标准函数对语言中的运算符和标准函数对应起来,当在程序中对于应起来,当在程序中对于cuberootcuberoot(y y)给出了定义之后,就可)给出了定义之后,就可以在主函数或其它用户定义的函数中,像运算符或标准函数那样以在主函数或其它用户定义的函数中,像运算符或标准函数那样使用了,如使用了,如cuberootcuberoot(x x)的使用与)的使用与a+ba+b, sinsin(x x)的使用没有)的使用没有什么区别。什么区别。 55.2 5.2 函数的说明与使用函数的说明与使用- - 参看书参看书p130p130的的5.25.2节节C+C+程程序序允允许许两两种种函函数数说说明明语语句句的的形形式式,我我们们把把它它们们分分别别称称为为函数原型(或函数声明)和函数定义。函数原型(或函数声明)和函数定义。 n1 1函数原型函数原型n函数原型(亦称函数声明)用来指出函数的名称,类型和参函数原型(亦称函数声明)用来指出函数的名称,类型和参数,其格式为:数,其格式为:属性说明属性说明类型类型函数名函数名(参数表参数表);); intint add ( add (intint a, a, intint b); b); inline void swap (float & s, float & t);inline void swap (float & s, float & t); void print (char *) void print (char *) ;65.2.1 5.2.1 函数的说明函数的说明 属属性性说说明明:可可缺缺省省,一一般般可可以以是是下下面面的的关关键键字字之之一一:inlineinline,staticstatic,virtual, friend virtual, friend 等。等。inline inline 表示该函数为内联函数;表示该函数为内联函数;static static 表示该函数为静态函数;表示该函数为静态函数;virtual virtual 表示该函数为虚函数;表示该函数为虚函数;friend friend 表示该函数为某类表示该函数为某类(class)(class)的友元函数。的友元函数。 类型:指函数的返回类型。类型:指函数的返回类型。 函数名:一个标识符。函数名:一个标识符。 参数表:它可能为空,参数表:它可能为空,void void 或或类型类型参数名参数名,类型类型参数名参数名 的形式。的形式。 main() main() ,print(voidprint(void) ) , cuberoot(floatcuberoot(float x) x) , add(intadd(int a,inta,int b) b) 75.2.1 5.2.1 函数的说明函数的说明n 2 2函数定义函数定义n函数定义与函数原型的主要区别是它还包括函数体,函数定义与函数原型的主要区别是它还包括函数体,其格式为:其格式为: 属性说明属性说明类型类型函数名函数名(参数表参数表)函数体函数体 属性说明,返回类型,函数名与函数原型一致,参属性说明,返回类型,函数名与函数原型一致,参数表中不可省略参数名。数表中不可省略参数名。 函数体函数体:由和括起来的复合语句即程序块。由和括起来的复合语句即程序块。program 5_1 program 5_1 的最后的最后12 12 行就是一个函数定义行就是一个函数定义。 8函数的分类方法函数的分类方法 1 1 从从使用角度使用角度分类分类 2 2 从从函数形式函数形式分类分类9 (1 1)从使用角度分类从使用角度分类 从从使使用用角角度度划划分分,可可将将函函数数分分为为:系系统统预预定定义义的的标标准准库库函函数数(如如,sinsin,absabs等等),以以及及由由用用户户自自定义定义的函数。的函数。 程程序序中中可可直直接接使使用用(调调用用)系系统统预预定定义义的的标标准准库库函函数数,但但要要求求在在调调用用前前使使用用编编译译预预处处理理指指令令includeinclude将对应的头文件包含进来。将对应的头文件包含进来。 由由用用户户自自定定义义的的函函数数与与系系统统预预定定义义的的标标准准库库函函数数的的不不同同点点在在于于,自自定定义义函函数数的的函函数数名名、参参数数个个数数、函函数数返返回回值值类类型型以以及及函函数数所所实实现现的的功功能能等等都都完完全全由由用户程序来规定(指定)用户程序来规定(指定)。10(2 2)从函数形式分类从函数形式分类 从从函函数数形形式式划划分分,可可分分为为无无参参函函数数与与有有参参函函数数两两类类。对对无无参参函函数数来来说说,调调用用它它们们时时不不需需要要提提供供实实际际参参数数;而而对对有有参参函函数数进进行行调调用用时时,必必须须提提供供所所需需个个数数的的且且具具有有相相匹匹配配数数据据类类型型的的实际参数。实际参数。11无参函数的定义无参函数的定义 。 无参函数定义无参函数定义的一般形式的一般形式 () () 1 . . n 通常用于通常用于实现某种固定功能实现某种固定功能。例如:。例如: void void printStarprintStar() . () . 就就自自定定义义了了一一个个叫叫做做printStarprintStar的的无无参参函函数数,比比如可用它来实现打印出一行共如可用它来实现打印出一行共1010个个“* *”的固定功能。的固定功能。12 有参函数定义有参函数定义的一般形式的一般形式 ( ( ) 1 . . n 通通过过调调用用处处提提供供的的不不同同实实参参值值来来计计算算出出其其对对应应的的函函数数值值、或或实现某种与传递过来的那些不同值有关的某种功能实现某种与传递过来的那些不同值有关的某种功能。例如:。例如: void void printStar(intprintStar(int k) . k) . 就就自自定定义义了了一一个个叫叫做做printStarprintStar的的具具有有1 1个个intint型型形形参参k k的的函函数,比如可用它来实现打印出一行共数,比如可用它来实现打印出一行共k k个个“* *”的自定义功能。的自定义功能。135.2.2 5.2.2 函数的调用函数的调用函函数数调调用用是是已已定定义义函函数数的的一一次次实实际际运运行行,与与某某类类型型的的一一个个变变量量和和后后文文中中某某类类的的一一个个对对象象类类似似, ,函函数数调调用用是是函函数数定定义义的的一一个个“实实例例”。在在C+C+程程序序中中,除除mainmain函函数数外外,其其它它任任一一函函数数的的执执行行都都是是通通过过在在mainmain函函数数中中直直接接或或间间接接地地调调用用该该函函数数而而引引发发的的。调调用用一一个个函函数数就就是是去去执执行行该该函函数数之之函函数数体体的的过过程。程。 对无参函数进行调用对无参函数进行调用的一般形式为:的一般形式为: ()() 例如:例如:printStarprintStar(); (); 14 对有参函数进行调用对有参函数进行调用的一般形式为:的一般形式为: ( ( ) ) 例如:例如:printStar(26);printStar(26); 其其中中函函数数说说明明中中的的参参数数称称为为形形式式参参数数(形形参参),函函数数调调用用中中的的参参数数称称为为实实际际参参数数(实实参参)。实实参参表表在在参参数数个个数数、参参数数顺顺序序、以以及及参参数数类类型型等等方方面面要要与与被被调调函函数数的的形形参参表表之之间间有有一一个个一一一一对对应应的的相相互互匹匹配配关关系系。编编译译器器将将根根据据参参数数的的顺顺序序,来来逐逐一一实实现现实实参参与与对对应应形形参参的的“结结合合”,而而后后执执行行一一遍遍函函数数体体(而而完成本次的函数调用)。完成本次的函数调用)。 15 计算机对计算机对函数进行调用函数进行调用的执行顺序的执行顺序以以Program5-1Program5-1为例为例(1) (1) 根据调用语句中的函数名根据调用语句中的函数名( (cuberootcuberoot) )在整个程序中搜索同名函数定在整个程序中搜索同名函数定义;义;(2) (2) 对实参数的参数个数,类型,顺序进行核对,判定是否与函数定义对实参数的参数个数,类型,顺序进行核对,判定是否与函数定义中的形参表对应一致,在上例中只有一个浮点型参数;中的形参表对应一致,在上例中只有一个浮点型参数;(3) (3) 根据参数的类型(值参数或引用参数)进行值参数的值传递或引用根据参数的类型(值参数或引用参数)进行值参数的值传递或引用参数的换名,在上例中即是要把实参表达式的值计算出来赋给形参参数的换名,在上例中即是要把实参表达式的值计算出来赋给形参x x; (4) (4) 运行函数体代码;运行函数体代码;(5) (5) 返回调用点,并返回所要求的函数值,即返回计算结果返回调用点,并返回所要求的函数值,即返回计算结果crootcroot 的值。的值。16 关于关于函数原型的一点说明函数原型的一点说明 当当函函数数调调用用从从书书写写顺顺序序上上先先于于函函数数定定义义时时,C+C+要求必须事先列出这一函数的函数原型。要求必须事先列出这一函数的函数原型。 (1 1)无参函数)无参函数定义的函数原型定义的函数原型 () (); (2 2)有参函数)有参函数定义的函数原型定义的函数原型 ( ( ) ); 注注意意,与与函函数数定定义义的的一一般般形形式式相相比比,相相当当于于用用分号代换了函数体,而成为其相应的函数原型。分号代换了函数体,而成为其相应的函数原型。17 5.2.3 5.2.3 函数的返回函数的返回函数的返回完成两项任务:函数的返回完成两项任务:n (1) (1) 把运行控制从函数体返回到函数调把运行控制从函数体返回到函数调用点。用点。 在在program5-1program5-1中就是在计算中就是在计算cuberootcuberoot (- (-q/2+a) q/2+a) 之后再返回到语句之后再返回到语句 xrxr = = cuberootcuberoot () + () + cuberootcuberoot () () 的的计算过程中。计算过程中。n(2) (2) 根据返回值要求,返回所需要的数根据返回值要求,返回所需要的数据值。据值。 18 5.2.3 5.2.3 函数的返回函数的返回n函数的返回值有下面几种情形:函数的返回值有下面几种情形:n1.1.返回返回void void 类型类型 如果函数无值返回,应说明为如果函数无值返回,应说明为void void 类型。未作类型说明的函数,系统类型。未作类型说明的函数,系统认为是认为是intint 类型函数,应返回一整型值。类型函数,应返回一整型值。 n2 2返回数值类型返回数值类型 最常见的函数是返回一个数值的函数。最常见的函数是返回一个数值的函数。 例如:例如: intint add add(intint a a,intint b b);); 当函数要返回的数值不止一个时,情况比较复杂,一般它可以以结构或当函数要返回的数值不止一个时,情况比较复杂,一般它可以以结构或类的形式,也可以以结构,数组或对象指针类型方式实现,这样的实例类的形式,也可以以结构,数组或对象指针类型方式实现,这样的实例在后面的章节可以见到。在后面的章节可以见到。n 3 3返回引用类型返回引用类型 值返回方式是值返回方式是C C 和和Pascal Pascal 语言中唯一的返回方式,语言中唯一的返回方式,C+C+语言提供的引用语言提供的引用返回概念是一种很强的功能,当函数定义中把该函数说明为某类型的引返回概念是一种很强的功能,当函数定义中把该函数说明为某类型的引用类型时,该函数调用后返回的不单是值,而是包含返回值的变量(或用类型时,该函数调用后返回的不单是值,而是包含返回值的变量(或对象)。由于返回引用与引用类型有关,所以这样的实例将在下节介绍。对象)。由于返回引用与引用类型有关,所以这样的实例将在下节介绍。 19函数应用实例函数应用实例1. 1. 实例实例1 - 1 - 无须给出函数无须给出函数f f的原型的原型 设设 f(x) = (x*x+x+1)/2-5.5f(x) = (x*x+x+1)/2-5.5。(1 1)求求z = (f(2.5)+2*f(6)/f(4.3)z = (f(2.5)+2*f(6)/f(4.3),并显示结果并显示结果z z。(2 2)对任意输入的一个实数对任意输入的一个实数a a,求出求出f(a)f(a)并显示。并显示。 程序执行后的交互结果如下:程序执行后的交互结果如下:z=4.90618z=4.90618Input a=Input a=1010f(a)=50f(a)=5020程序编制:程序编制:# #include include double f (double x)double f (double x)double y;double y;y=(x*x+x+1)/2-5.5;y=(x*x+x+1)/2-5.5;return (y); return (y); / /对非对非voidvoid类型的函数,必须有一个类型的函数,必须有一个 /return /return语句,由它返回函数值语句,由它返回函数值 21 void main()void main()double z,a;double z,a;z=(f(2.5)+2*f(6)/f(4.3); /z=(f(2.5)+2*f(6)/f(4.3); /调用自定义函数调用自定义函数f fcoutcoutz=zz=zendlendl; ;coutcoutInput a=; a;a;coutcoutf(a)=f(a)f(a)=f(a)endlendl; /; /算出算出f(a)f(a)并输出并输出 22点评:点评: 1 1)mainmain中共出现了中共出现了4 4次对自定义函数次对自定义函数f f的调用。的调用。 2 2)编写编写f f函数时的函数时的3 3点注意:点注意: (1 1)f f函数体内的函数体内的3 3行也可用如下的一行来代替行也可用如下的一行来代替 return (x*x+x+1)/2-5.5); return (x*x+x+1)/2-5.5); return return句括号内表达式的值,即为整个函数的返句括号内表达式的值,即为整个函数的返回值。回值。 (2 2)returnreturn句也可使用另一格式,即可以不括起句也可使用另一格式,即可以不括起表达式:表达式: return (x*x+x+1)/2-5.5; /OK!return (x*x+x+1)/2-5.5; /OK!23(3 3)三种不正确的用法三种不正确的用法 f=(x*x+x+1)/2-5.5;f=(x*x+x+1)/2-5.5; 不可给函数名不可给函数名f f赋值。赋值。 return (f);return (f); 返回值类型应该是返回值类型应该是doubledouble,而非指针类型(函而非指针类型(函数名相当于一个指针)。数名相当于一个指针)。 f(x)=(x*x+x+1)/2-5.5;f(x)=(x*x+x+1)/2-5.5; 赋值号左端非变量(也即赋值号左端非变量(也即f(x)f(x)非左值)。非左值)。 24 3 3)关于关于returnreturn语句语句returnreturn语句(称为返回语句)具有如下三种使用格式:语句(称为返回语句)具有如下三种使用格式:return;return;return return ;return ( return ( ); );第一种第一种格式的格式的returnreturn用于立即从被调函数中返回用于立即从被调函数中返回, , 当函当函数数类型为类型为voidvoid时,应使用这种格式的返回语句。时,应使用这种格式的返回语句。 当函数当函数类型为非类型为非voidvoid型时,应使用型时,应使用第二或第三种第二或第三种格式的格式的returnreturn语句,此两种格式的语句效果完全相同(可将第二种语句,此两种格式的语句效果完全相同(可将第二种格式看成是第三种格式的省略形式),系统此时都将计算出格式看成是第三种格式的省略形式),系统此时都将计算出表达式的值,并表达式的值,并“携带携带”该值立即从被调函数中返回。该值立即从被调函数中返回。25 2. 2. 实例实例2 - 2 - mainmain在前而被调函数在前而被调函数f f 在后时,必须先列出函数在后时,必须先列出函数f f的原型的原型 设设 f(x) = (x*x+x+1)/2-5.5f(x) = (x*x+x+1)/2-5.5。 (1 1)求求z = (f(2.5)+2*f(6)/f(4.3)z = (f(2.5)+2*f(6)/f(4.3),并显示结果并显示结果z z。 (2 2)对任意输入的一个实数对任意输入的一个实数a a,求出求出f(a)f(a)并显示。并显示。 程序执行后的交互结果如下:程序执行后的交互结果如下:z=4.90618z=4.90618Input a=Input a=1010f(a)=50f(a)=5026程序编制:程序编制:# #include include double f (double x); double f (double x); /函数函数f f的原型的原型void main()void main() /同上,从略同上,从略 double f (double x) double f (double x) /被调函数被调函数f f的具体定义的具体定义 /同上,从略同上,从略 273. 3. 实例实例3 - 3 - 无参函数,实现固定功能无参函数,实现固定功能 编无参函数,编无参函数,void void printStarprintStar()(),它负责它负责完成完成固定功能固定功能:在同一行连续显示:在同一行连续显示6060个个“*”“*”。并编制主。并编制主函数函数mainmain,对该函数实现调用,使程序执行后的显对该函数实现调用,使程序执行后的显示结果如下:示结果如下:the result of first call to the result of first call to printStarprintStar():():*the result of second call to the result of second call to printStarprintStar():():* * 28程序编制:程序编制: # #include include void void printStarprintStar() () /自定义无参函数自定义无参函数printStarprintStarfor(intfor(int i=1; i=60; i+) / i=1; i=60; i+) /显示显示6060个个“*”“*”coutcout*;*;coutcoutendlendl; ;return; return; 29 void main()coutthe result of first call to printStar():endl;printStar();/对对printStar的第一次调用的第一次调用coutthe result of second call to printStar():endl;printStar();/对对printStar的第二次调用的第二次调用 30点评:点评: (1 1)将将printStarprintStar定为定为voidvoid类型类型,是因为,是因为不需要它返回不需要它返回任何值任何值。(2 2)按语句方式调用按语句方式调用printStarprintStar(而不可作为表达式因而不可作为表达式因子,如子,如3+3+printStar()/2printStar()/2等)。等)。(3 3)printStarprintStar函数中的函数中的returnreturn用于立即从被调函数中用于立即从被调函数中返回到主调函数处(跳转到调用语句的下一语句处)去执行,返回到主调函数处(跳转到调用语句的下一语句处)去执行,当函数类型为当函数类型为voidvoid时,应使用这种无返回值的时,应使用这种无返回值的returnreturn语句。语句。实际上,由于本函数是实际上,由于本函数是在执行完函数体内的所有语句后才返在执行完函数体内的所有语句后才返回回,此种情况下的,此种情况下的returnreturn语句可以缺省语句可以缺省。31 4. 4. 实例实例4 - 4 - 一参函数一参函数 编一参函数,编一参函数,void void printStar(intprintStar(int k) k),它负它负责责显示出显示出k k行行“*”“*”来,其中每行均显示连续的来,其中每行均显示连续的8 8个个“*”“*”。并编制主函数。并编制主函数mainmain,对该函数实现调用,对该函数实现调用,使程序执行后的显示结果样式如下:使程序执行后的显示结果样式如下:k=? k=? 3 3*32程序编制:程序编制:#include void printStar(int k) /k/k为形参,由调用处的实参提供实际值为形参,由调用处的实参提供实际值for(int i=1; i=k; i+) /显示出显示出k k行行 cout*endl;return; /该该returnreturn语句可以缺省语句可以缺省 void main()int k; /欲显示的行数欲显示的行数k kcoutk;printStar(k); /函数调用,带去输入的实参函数调用,带去输入的实参k k 33 5. 5. 实例实例5 - 5 - 二参函数二参函数 编二参函数,编二参函数,void void printStar(intprintStar(int k, k, intint n)n),它负责它负责显示出显示出k k行行“*”“*”来,且来,且每行每行均均显示显示连续的连续的n n个个“*”“*”。并编制主函数,对该函数实。并编制主函数,对该函数实现调用,使程序执行后的显示结果样式如下:现调用,使程序执行后的显示结果样式如下:k,n=? k,n=? 4 154 15*34程序编制:程序编制:#include void printStar(int k, int n) /负责显示出负责显示出k行行*来,且每行均显示连续的来,且每行均显示连续的n个个*for(int i=1; i=k; i+) /循环循环k次,显示出次,显示出k行行“*”for(int j=1; j=n; j+) /循环循环n次,显示出次,显示出1行的行的n个个“*”cout*;coutendl;/注意,函数末右花括号前缺省了一个注意,函数末右花括号前缺省了一个return语句语句35 void main()void main()intint k,n; / k,n; /显示出显示出k k行,每行显示行,每行显示n n个个“*”“*”coutcoutk,n=? ;kn; kn; printStar(kprintStar(k, n); , n); / /以输入的以输入的k k与与n n为实参去调用为实参去调用printStarprintStar 36 6. 6. 实例实例6 - 6 - 无参函数,全局变量无参函数,全局变量 编无参函数,编无参函数,void void printStarprintStar()(),并并结合使用结合使用全局变量全局变量k k与与n n,使每调用一次该函数,总使每调用一次该函数,总显示出显示出k k行行“*”“*”来,且来,且每行每行均均显示显示连续的连续的n n个个“*”“*”,并编,并编制主函数,对该函数实现调用,使程序执行后的显制主函数,对该函数实现调用,使程序执行后的显示结果样式如下:示结果样式如下:k,n=? k,n=? 3 203 20* * 37程序编制:程序编制: # #include include intint k,n; k,n; /intint型型全局变量全局变量k k,n n /* /* 在在所有函数之外所有函数之外(即在不属于任一函数定义的外(即在不属于任一函数定义的外面)面)说明的变量为说明的变量为全局变量全局变量,其作用域即有效区域,其作用域即有效区域为整个文件(或具有多个文件的整个程序,详细请为整个文件(或具有多个文件的整个程序,详细请参看本章的参看本章的5.75.7及及5.85.8节)。节)。 */ */38 void void printStarprintStar() /() /无参函数无参函数printStarprintStar /其中所用的其中所用的k k与与n n都是全局变量都是全局变量for(intfor(int i=1; i=k; i+) i=1; i=k; i+)/显示出显示出k k行行“*” “*” for(intfor(int j=1; j=n; j+) j=1; j=n; j+) /显示显示n n个个“*” “*” coutcout*;*; coutcoutendlendl; ; 39void main()coutkn;/输入全局变量输入全局变量k,全局变量全局变量n的值的值coutthe result of call to printStar():endl;printStar(); /调用无参函数调用无参函数printStar 405.5.2.4 2.4 函数的参数函数的参数 C+C+语言允许函数无参,有一个或多个参数,而且还支持不定个数参数的函数。语言允许函数无参,有一个或多个参数,而且还支持不定个数参数的函数。 (1)(1)无参函数:用无参函数:用void void 或空表示无参。或空表示无参。 (2)(2)一个或多个参数:多数函数有一个或多个确定的个数、确定的类型和顺序的参一个或多个参数:多数函数有一个或多个确定的个数、确定的类型和顺序的参数。数。 在在C+C+语言中,不同的函数是根据函数名和参数表二者来区分的,只有二者完语言中,不同的函数是根据函数名和参数表二者来区分的,只有二者完全一致才是同一函数。全一致才是同一函数。 (3 3)不定个数参数:)不定个数参数: 有些应用问题中参数个数是变化的。有些应用问题中参数个数是变化的。 例如设计一个电话计费函数,为了计算通话费,不同的通话类型(如市话,例如设计一个电话计费函数,为了计算通话费,不同的通话类型(如市话,长途,数据与通讯,长途,数据与通讯,BP BP 机等)有不同数目的参数。机等)有不同数目的参数。 处理参数个数不定的情形,可有不同的途径:处理参数个数不定的情形,可有不同的途径:例如指针,例如指针,C+C+提供的某些库函数,提供的某些库函数,无名参数,可缺省参数等。无名参数,可缺省参数等。 41 无名参数无名参数 -n C+ C+ 语言语言, ,允许参数表中包含无名参数,主要是允许参数表中包含无名参数,主要是为了区分函数,为了区分函数, 例如:例如: intint f f(intint a a,intint b b)return areturn ab*bb*b; intint f f(intint a a,intint b b,intint)return a*areturn a*ab b; 两个不同的函数同名,但由于第二个函数包含一无两个不同的函数同名,但由于第二个函数包含一无名参数,使得在调用时能够被区分,名参数,使得在调用时能够被区分,f f(x x,y y)是第)是第一个函数的调用,一个函数的调用,f f(x x,y y,0 0)是第二个函数的调)是第二个函数的调用。用。42 可缺省参数(参数默认值)可缺省参数(参数默认值) - 允许在允许在函数定义处函数定义处为其中为其中最后面的连续若干个参数最后面的连续若干个参数设置设置默认值默认值(也称(也称缺省值缺省值),从而为调用处提供了方便(若调用),从而为调用处提供了方便(若调用处缺省了某个或某些实参的情况下,系统将自动使用那些在处缺省了某个或某些实参的情况下,系统将自动使用那些在函数定义处给定的参数默认值)。函数定义处给定的参数默认值)。 下面是一个示例性程序,在定义函数下面是一个示例性程序,在定义函数funcfunc时为它的最后时为它的最后面的连续面的连续3 3个参数(也即它的所有参数)都设置了默认值。若个参数(也即它的所有参数)都设置了默认值。若调用处缺省调用处缺省了某个或某些了某个或某些实参实参的情况下,系统将的情况下,系统将自动使用自动使用这这些给定的参数些给定的参数默认值默认值。43#include void func(int a=11, int b=22, int c=33) /为参数为参数a、b、c设置了默认值设置了默认值11、22与与33couta=a, b=b, c=cendl; 44void main()void main() funcfunc();(); / /调用时调用时缺省了缺省了3 3个实参个实参,将使用,将使用 / /定义处给定的那定义处给定的那3 3个相对应的参数默认值个相对应的参数默认值 func(55);func(55); / /调用时调用时缺省了后缺省了后2 2个实参个实参,将使用,将使用 / /定义处给定的那后定义处给定的那后2 2个对应参数默认值个对应参数默认值 func(77,99);func(77,99); / /调用时调用时缺省了最后缺省了最后1 1个实参个实参,将使用,将使用 / /定义处给定的那最后定义处给定的那最后1 1个参数默认值个参数默认值 func(8,88,888);func(8,88,888); / /调用时调用时没缺省任一个实参没缺省任一个实参,系统将,系统将 / /不使用定义处给定的任一个参数默认值不使用定义处给定的任一个参数默认值 45程序执行后的显示结果如下:程序执行后的显示结果如下: a=11, b=22, c=33a=11, b=22, c=33a=55, b=22, c=33a=55, b=22, c=33a=77, b=99, c=33a=77, b=99, c=33a=8, b=88, c=888a=8, b=88, c=88846 注意,只能为函数注意,只能为函数最后面的连续若干个最后面的连续若干个参数设置默认值,且在参数设置默认值,且在调用处调用处也只能缺省也只能缺省后面的连续若干个后面的连续若干个实参。实参。 void void func(intfunc(int a, a, intint b=2, b=2, intint c=3); c=3); /OK!/OK! void void func(intfunc(int a=1, a=1, intint b, b, intint c=3); c=3); /ERROR!/ERROR! 对第一个函数说明,采用如下的调用语句:对第一个函数说明,采用如下的调用语句: func(1, 22, 333); func(1, 22, 333); /OK! /OK! 调用时给出所有实参调用时给出所有实参 funcfunc(); (); / ERROR! / ERROR! 参数参数a a没有默认值没有默认值 func(10, 20); func(10, 20); /OK! /OK! 参数参数c c默认为默认为3 3 func(5, , 9); func(5, , 9); / ERROR! / ERROR! 调用处也只能缺省后面的连续若干个实参调用处也只能缺省后面的连续若干个实参 475.5.2.5 2.5 值调用和引用调用值调用和引用调用C+C+语言在进行函数调用时,对参数的处理有两种方式,赋值语言在进行函数调用时,对参数的处理有两种方式,赋值型和引用型,即值调用方式和引用调用方式。前者是普通的形式,型和引用型,即值调用方式和引用调用方式。前者是普通的形式,在在C C 语言中只有这种方式;语言中只有这种方式;C+C+语言中增加了引用调用形式,这种语言中增加了引用调用形式,这种形式与形式与pascalpascal 语言中的变量参数调用方式相似。语言中的变量参数调用方式相似。 1 1 赋值调用赋值调用2 2 引用调用引用调用 48赋值调用赋值调用 - -赋值形参:在函数定义的参数中,除了被说明为引用(赋值形参:在函数定义的参数中,除了被说明为引用(& &)的参数之外,其)的参数之外,其余所有类型的形参都属于赋值形参。余所有类型的形参都属于赋值形参。过程:过程:在执行函数调用时,在检查函数名及参数表之后,首先为值参数分在执行函数调用时,在检查函数名及参数表之后,首先为值参数分配内存,然后计算各对应的实参表达式,并把计算的值赋给刚刚创建的配内存,然后计算各对应的实参表达式,并把计算的值赋给刚刚创建的参数变量,进而开始函数体的运行。参数变量,进而开始函数体的运行。 凡是赋值形参,在函数的每次调用时,凡是赋值形参,在函数的每次调用时,都必须为每一个赋值形参创建一个都必须为每一个赋值形参创建一个新的参数变量。新的参数变量。n实参表达式:另一方面,函数调用语句中,与赋值形参相对应的实参可实参表达式:另一方面,函数调用语句中,与赋值形参相对应的实参可以是指定类型的常量、变量或表达式。在执行函数调用时应把该表达式以是指定类型的常量、变量或表达式。在执行函数调用时应把该表达式的值计算出来,作为初值赋给刚刚为赋值形参创建的参数变量。这是赋的值计算出来,作为初值赋给刚刚为赋值形参创建的参数变量。这是赋值调用方式名称的由来。值调用方式名称的由来。n为赋值形参创建的参数变量是局限于函数体运行的局部变量,它作为该为赋值形参创建的参数变量是局限于函数体运行的局部变量,它作为该形参的一个实例,参加函数体程序块的这次运行,形参的一个实例,参加函数体程序块的这次运行,一旦运行完毕,这个一旦运行完毕,这个参数变量就被撤消参数变量就被撤消。 49引用调用引用调用函数定义的参数表中,名字前加上符号的参数为引用形参。函数定义的参数表中,名字前加上符号的参数为引用形参。 n例如例如void swapvoid swap(intint a a,intint b b););引用形参在调用过程中的参数传递机制不同于赋值形参。其要点是:引用形参在调用过程中的参数传递机制不同于赋值形参。其要点是:(1 1)函数的调用语句中对应于引用形参的实参必须是同一类型的变量,非)函数的调用语句中对应于引用形参的实参必须是同一类型的变量,非变量的表达式则不允许。变量的表达式则不允许。(2 2)参数传递的内容不是实参的值,而是地址,其实际的效果是令对应的)参数传递的内容不是实参的值,而是地址,其实际的效果是令对应的引用形参在调用过程中,作为一个变量名指向作为实参的这个变量,引用形参在调用过程中,作为一个变量名指向作为实参的这个变量,与赋值形参的不同在这里体现出来,在引用调用过程中并不创建新的与赋值形参的不同在这里体现出来,在引用调用过程中并不创建新的参数变量!参数变量!(3 3)在函数体程序块的运行中,引用形参的每次出现,由于它现在已经是)在函数体程序块的运行中,引用形参的每次出现,由于它现在已经是指向实参变量,因此相当于全用实参变量所代替。即起到了所谓的指向实参变量,因此相当于全用实参变量所代替。即起到了所谓的“换名换名”的作用。的作用。(4 4)在函数体程序运行结束,控制转回调用点时,该引用形参与实参变量)在函数体程序运行结束,控制转回调用点时,该引用形参与实参变量的对应关系也就终止了。但是在调用过程中对于这个实参变量的所有的对应关系也就终止了。但是在调用过程中对于这个实参变量的所有处理和操作的结果,却保留下来。这一点也是区别于赋值调用的。处理和操作的结果,却保留下来。这一点也是区别于赋值调用的。 50引用调用引用调用n设计函数在下面两种情形时,设计函数在下面两种情形时, 需要改变某些变量的值(上述函数需要改变某些变量的值(上述函数swap swap 就是一就是一例);例); 对于占内存较多的数据参数,为了不另建新的参对于占内存较多的数据参数,为了不另建新的参数变量以节省内存,建议采用引用参数。数变量以节省内存,建议采用引用参数。 在后一种情况,为了保证实参不在函数中被修改,在后一种情况,为了保证实参不在函数中被修改,可在形参说明中加上可在形参说明中加上const const 说明,例如:说明,例如: complex add complex add(const complexconst complex a a,const complexconst complex b b);); 而对于赋值形参,则无此必要。而对于赋值形参,则无此必要。51实例实例 void void printStar(intprintStar(int k, k, intint n); n); /它所用的两个参数均为它所用的两个参数均为赋值参数赋值参数。void void swap(intswap(int& x, & x, intint& y); & y); /它所用的两个参数均为它所用的两个参数均为引用参数引用参数。intint myFunc(intmyFunc(int a, float& b); a, float& b); /它所用的第一个参数为赋值参数它所用的第一个参数为赋值参数/另一个为引用参数。另一个为引用参数。 语法要求:调用函数时,语法要求:调用函数时,引用参数对应的实参引用参数对应的实参必须是指定类型的变量必须是指定类型的变量。 52 对函数进行调用的执行过程(步骤)对函数进行调用的执行过程(步骤): (1 1) 将对应将对应实参表达式实参表达式的的值值赋给赋给赋值赋值形参形参(若(若参数为赋值参数的话);参数为赋值参数的话); (2 2) 用用实参变量实参变量替换替换相应的相应的形参形参(若参数为引(若参数为引用参数的话);用参数的话); (3 3) 按各形参的按各形参的“当前值当前值”(或已被(或已被“赋值赋值”,或已被,或已被“换名换名”)去)去执行执行一遍一遍函数体函数体并并返回返回调用调用处。处。53注意:注意: (1 1) 通过通过赋值参数赋值参数来传值的方式是来传值的方式是一种一种“单向传值单向传值”方式,它只可向被调方式,它只可向被调函数的形参函数的形参“传入传入”值,而不可通过该值,而不可通过该形参形参“传出传出”值。值。 (2 2) 通过通过引用参数引用参数来传值的方式是来传值的方式是一种一种“双向传值双向传值”方式,它不仅可向被方式,它不仅可向被调函数的形参调函数的形参“传入传入”值,而且还可通值,而且还可通过该形参过该形参“传出传出”值。值。 54实例实例 通过下面的小例子来理解赋值参数与引用参数通过下面的小例子来理解赋值参数与引用参数的使用区别。的使用区别。#include void f1(int& a, int& b, int c, int d); /函数原型函数原型 /前两个为引用参数,可前两个为引用参数,可“双向传值双向传值” /后两个为赋值参数后两个为赋值参数55 void main() int i=1, j=2, k=77, n=88;cout- In main, befor calling f1 -endl;couti, j, k, n = i j k nendlendl;f1(i, j, k, n); /注意,调用后实参变量注意,调用后实参变量i i、j j的值进行了改变的值进行了改变/而而k k与与n n的值并不改变的值并不改变cout- In main, after calling f1 -endl;couti, j, k, n = i j k nendl;56 void f1(int& a, int& b, int c, int d) /* /* 前两个为引用参数,后两个为赋值参数。对前两个为引用参数,后两个为赋值参数。对引用参数引用参数而言,而言,调用时,将用对应实参变量来替换它们。即是说,调用时,将用对应实参变量来替换它们。即是说,被调函数被调函数中中对对形参值形参值的使用与改变,就是对的使用与改变,就是对主调函数主调函数中调用语句处所对应中调用语句处所对应实参变量值实参变量值的直接使用与改变(的直接使用与改变(“双向传值双向传值”) */ */ cout- Enter f1 -endl; couta, b, c, d = a b c dendlendl; int tmp;tmp=a; a=b; b=tmp; /交换交换a a与与b b的值的值tmp=c; c=d; d=tmp; /交换交换c c与与d d的值的值cout- In the end of f1 -endl; couta, b, c, d = a b c dendlendl; 57程序执行后的显示结果如下:程序执行后的显示结果如下:- - In main, In main, beforbefor calling f1 - calling f1 -i, j, k, n = 1 2 77 88i, j, k, n = 1 2 77 88- Enter f1 - Enter f1 -a, b, c, d = 1 2 77 88a, b, c, d = 1 2 77 88- In the end of f1 - In the end of f1 -a, b, c, d = 2 1 88 77a, b, c, d = 2 1 88 77- In main, after calling f1 - In main, after calling f1 -i, j, k, n = 2 1 77 88i, j, k, n = 2 1 77 88585.2.6 5.2.6 内联函数内联函数 - - 可在一般的函数说明前冠以关键字可在一般的函数说明前冠以关键字inlineinline,称这样的函数为称这样的函数为内联函数内联函数。按如下的方式来说。按如下的方式来说明:明:inline inline ( ( ) ) 59 在编译过程中,凡内联函数,在编译过程中,凡内联函数,系统均把它的系统均把它的执行代码插入到该函数的每个调用点处执行代码插入到该函数的每个调用点处( (以取以取代那一函数调用代那一函数调用) ),从而使程序执行过程中,从而使程序执行过程中,每次对该函数调用时不需控制转移,可每次对该函数调用时不需控制转移,可节省节省执行时间执行时间; ; 但由于每个调用点处均出现那一但由于每个调用点处均出现那一函数的执行代码拷贝,相对来说使用内联函函数的执行代码拷贝,相对来说使用内联函数后数后会扩大其代码空间会扩大其代码空间。 60使用内联函数的简单实例使用内联函数的简单实例# #include include inline inline intint max(intmax(int x, x, intint y) / y) /内联函数内联函数maxmax return(xy?x:y);return(xy?x:y); 61 void main(void)void main(void) intint a,b; a,b; coutcoutInput a,b:;ab;ab;coutcoutmax(a,b)=max(a,b)max(a,b)=max(a,b)endlendl; ; /对内联函数对内联函数maxmax的调用的调用 62 程序执行后的显示结果如下:程序执行后的显示结果如下:Input a,b:123 456Input a,b:123 456max(a,b)=456max(a,b)=456 使用内联函数时应注意:使用内联函数时应注意: 1.1. 内联函数的函数体一般讲不宜过大内联函数的函数体一般讲不宜过大, , 以以1-51-5行为宜。行为宜。 2.2. 凡在类体中定义的成员函数凡在类体中定义的成员函数( (见第见第7 7章章) )均均隐含为内联函数。隐含为内联函数。 63第五章前部分结束第五章前部分结束 64
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号