C++Programming函数和编译预处理第5章引言函数的定义和调用函数的原型说明与值调用函数的嵌套和递归调用作用域和存储类内联函数具有缺省参数值的函数函数重载编译预处理程序的多文件组织C++Programming引言随着模拟对象的复杂性增加,程序将会变得越来越复杂和冗长。在编写一个较大的程序时,为了便于管理,可以采用一些较好的编程策略,常用的方法是按功能或操作将程序分割成一些具有特定功能的、相对独立的且便于管理和阅读的小模块。本章主要介绍这种分割工具之一:函数,包括函数的定义、函数的调用、参数的传递方法、内联函数、函数的重载、变量的作用域等,另外简要介绍编译预处理以及程序多文件组织的编译和连接方法。C++Programming函数的定义和调用函数概述函数定义函数调用5.1C++Programming函数概述C++中的函数就是具有特定功能的模块。函数是构成C++程序的基本单位,C++程序的运行都是由主函数(main())开始,然后通过一系列函数调用来实现各种功能。从用户角度看,函数包括用户自定义函数和系统库函数。从函数的形式来看,函数可以分为无参函数、有参函数、无返回值函数和有返回值函数等。除了main()函数以外,一个函数既可以被其它函数调用,也可以调用其它函数。图5-l反映了函数的层次组织结构以及相互之间的调用关系。5.1.1C++Programming系统库函数库函数也称为标准函数,是在C++编译系统中已经预先定义的函数。C++把一些常用的操作以库函数的方式提供给用户,包括常用的数学计算函数(如:sqrt()、fabs()等)、图形处理函数、标准输入/输出函数等。按功能对库函数进行分类,将同类库函数集中在一个头文件中,用户只要在程序中包含相应的头文件,就可以使用该头文件中的所有库函数。C++Programming用户自定义函数在程序设计过程中,用户可根据自己的需要将一段完成功能相对独立的代码定义为一个函数,这类函数称为用户自定义函数。本章将主要介绍用户自定义函数的定义和调用方法。C++Programming图5-1函数调用和被调用的层次关系main()Func1()Func5()Func3()Func2()Func4()C++Programming函数定义对于库函数,在头文件中已经定义好了,调用函数前只需包含相应的头文件即可;对于用户自定义的函数,要先完成函数的定义,然后才可以调用它。根据函数定义和使用时参数的不同,可将函数分为两类:无参函数和有参函数。无参函数有参函数函数返回值和return语句5.1.2C++Programming无参函数定义无参函数的一般格式为:《类型》函数名(《void》){…}//函数体类型为函数返回值的类型,它可以是任一标准数据类型或导出数据类型,当没有返回值时,《类型》必须为void。函数名为用户给函数起的名字,其命名规则与标识符相同。函数名后的括号”()”称为函数调用运算符,对于无参函数,函数调用运算符内可以为空,也可以为void。函数返回值为整型时,可省略类型标识符int。当函数无返回值时,必须规定其类型为void。示例C++Programming示例voidMessage(void){cout”*************\n”;cout”verygood!\n”;cout”*************\n”;}该函数完成输出一些问候语。像这类与外部环境之间没有任何数据交换的函数,通常将其定义为无参函数。C++Programming有参函数定义有参函数的一般格式为:《类型》函数名(形参表){…}//函数体有参函数中《类型》与函数名的含义和要求与无参函数一致。形参表为该函数的参数的类型和名字,形参表中的参数称为形式参数或形参,形参的个数是没有限制的,当超过一个参数时,参数间一定要用逗号”,”分隔开,且每个参数都要有类型说明。示例在定义有参函数时,必须标明每个参数的类型,即使参数的数据类型相同,也不能将参数合在一起用一个类型说明符。C++Programming示例例如,求两个整数中的大数,函数可定义为:intMax(intx,inty)//A{return(xy?x:y);}该函数有两个整型参数x,y,函数的返回值是整型。如上例A行写成以下形式就是错误的:Max(intx,y)C++Programming函数返回值和return语句函数的返回值也称为函数值。当函数有返回值时,在函数体中必须使用return语句来返回该函数的值。return语句的一般格式为:return表达式;或return(表达式);这里表达式可以为任意合法的表达式。当执行该语句时,首先求出表达式的值,再将该值转换成函数定义时规定的返回值的类型后,将其作为函数的返回值。【例5.1】求三角形的面积C++Programming函数调用C++中,函数的功能是通过在程序中对其调用来实现的。调用一个函数,就是把控制权转去执行该函数的函数体,函数体执行完之后,再将控制权转到调用函数处。无参函数的调用格式有参函数的调用格式函数调用的使用方式关于形参和实参的几点说明【例5.2】输入三个实数,求出其中的最大数图5-25.1.3C++Programming无参函数的调用格式无参函数的调用格式一般为:<函数名>()在调用无参函数前,必须先定义与它同名的无参函数。如:Message函数定义过之后,可以使用Message();来调用。C++Programming有参函数的调用格式有参函数的调用格式一般为:<函数名>(实参表)实参表中的参数称为实际参数或实参,每个实参可以是任一合法的表达式。实参与形参不同,在实参前没有数据类型说明符。在调用有参函数前,也必须先定义,另外,调用有参函数时实参表和形参表的参数类型、个数必须匹配。如:Max(4,9);及Max(a*4,b);函数的调用过程是:先计算各实参表达式的值(对有参函数),然后将所求的值传递给相应的形参,执行函数体,执行完毕再返回到函数的调用处,继续执行其后继语句。C++Programming函数调用的使用方式对于有返回值的函数,可以用两种方式调用:一种方式是函数调用出现在表达式中,其值(也称为函数值)参与表达式的运算;另一种方式是用一个语句来实现调用,即在调用格式后加上一个分号,构成函数调用语句,在这种情况下,函数返回值不起任何的作用。对于没有返回值的函数,函数调用只能通过函数调用语句实现。C++Programming关于形参和实参的几点说明定义函数时指定的形参,在未出现函数调用时,它们不占用内存中的存储单元。只有在函数调用时,形参才被分配内存单元,在调用结束后,形参所占的内存单元也被释放。调用时是将实参的值传递给形参,只是一个单向的传递关系。这是我们所说的“值传递”,形参值的改变不会影响实参的值。实参与形参的类型应相同。当类型不一致时,则它们应该兼容,即按照不同类型数值的赋值规则,将实参转换为形参的数据类型后再传递给形参。C++Programming图5-2函数的调用过程main()函数调用函数Max(a,b)函数Max(int,int)执行函数Max(int,int)的函数体将Max(a,b)的返回值赋给t调用函数Max(c,t)…结束向Max函数传递实参a,b向Max函数传递实参c,tC++Programming函数的原型说明与值调用函数的原型说明函数的值调用5.2C++Programming函数的原型说明C++中,与变量一样,函数的定义和使用也必须遵循先定义后使用的原则。如果函数的调用在函数的定义之前,就会出现编译错误。如果函数调用在前、定义在后,则在函数调用之前,必须要对被调用的函数作原型说明。函数原型说明是一条语句,它必须以分号结束。函数原型说明的一般格式函数原型说明的目的是告诉编译程序,该函数的参数个数、各参数的类型和返回值类型。函数原型说明可以出现在程序中的任何位置,只要在调用前即可,且对函数原型说明的次数没有限制。5.2.1C++Programming函数原型说明的一般格式《类型》函数名(形参表);或《类型》函数名(形参类型说明表);函数原型说明的形参和返回值的类型必须与对应的函数定义一致,对于第一种格式,原型说明中形参表的形参名可以和定义时的形参名不一致,又由于形参在原型说明中并不起任何作用,因此可以省略形参名(即第二种格式)。C++Programming函数的值调用C++中,形参与实参的结合方式有三种:传值调用、传地址调用和引用调用。传值调用简称为值调用。值调用的特点是:在被调用函数的执行过程中,只能改变形参,不能改变实参。【例5.3】值传递应用——两数交换图5-3值传递的好处是使得函数具有完全的独立性,函数的执行对其外界的变量没有影响。在值传递的情况下,函数只能通过return语句返回一个值或不返回任何值。要用函数实现两个数参数的交换,值传递是无法实现的,可以用地址传递或引用传递的方法。5.2.2C++Programming图5-3两数交换函数调用过程示意图35353553axbyybxa(a)交换前(b)交换后执行函数Swap(int,int)C++Programming函数的嵌套和递归调用函数的嵌套调用函数的递归调用5.3C++Programming函数的嵌套调用C++语言的函数之间是相互平行、独立的,在定义一个函数时,不允许在其函数体内再定义另一个函数,即函数不允许嵌套定义。但是函数之间的嵌套调用是可以的,即在定义一个函数时,在函数体内又调用另一个函数。图5-4【例5.4】求一元二次方程ax2+bx+c=0的根5.3.1C++Programming图5-4嵌套调用⑤⑥④③⑦②⑧main函数b函数a函数调用a函数结束调用b函数⑨①C++Programming函数的递归调用在函数A的定义中调用函数A,或在函数A的定义中调用函数B,而在函数B的定义中又调用了函数A,这类函数的自调用关系称为递归调用。前一种情况称为直接递归,而后一种情况称为间接递归。在C++语言中,这两种递归调用都是允许的。【例5.5】用函数递归调用方法求5!【例5.6】将参数逐位正序和反序输出【例5.7】Hanoi(汉诺塔)问题5.3.2C++Programming【例5.5】用函数递归调用方法求4!分析:由递推公式n!=n*(n-1)!,所以求n!的问题可以转化为求(n-1)!的问题,(n-1)!=(n-1)*(n-2)!,因此求(n-1)!的问题可以转化为求(n-2)!的问题,依此类推,直到转化为求1!的问题,根据定义,1!=1,则从1!=1开始将上述过程逆向求解,就可以求出n!。这种求解问题的方法可用函数递归调用方法实现算法演示程序代码C++Programming算法演示C++Programming程序代码#includeiostream.hintf(intn){if(n==0||n==1)return1;//Aelsereturnn*f(n-1);//B}voidmain(void){coutf(4)'\n';}C++Programming【例5.6】将参数逐位正序和反序输出设计两个函数,其参数都为整型,分别将参数逐位正序和反序输出。要求用递归函数实现。分析:要实现逐位正序或反序输出一个整数,则须获取它的各位数字。如1234,则用1234%10获得它的个位数,求十位数,则须先将个位数去掉,即1234/10,结果为123,再求个位数即可,依此类推。求一个整数K的各位数字时其递推关系为:f1=Ktn=fn%10fn+1=fn/10其中tn为整数K的第n位数字(从右数起),fn为去掉K的右边n-1位数后剩下的数。而该递推关系的结束条件为fn=0。程序代码C++Programming【例5.7】Hanoi(汉诺塔)问题这是一个经典的数学问题:有三个塔A、B、C,开始时A塔上有n个盘子,盘子大小不等,大的在下,小的在上,如图5-6所示。要求将这n个盘子从A塔移到C塔,但每次只允许移一个盘子,而