第4章函数与预处理4.1概述4.2定义函数的一般形式4.3函数参数和函数的值4.4函数的调用4.9函数的嵌套调用4.10函数的递归调用4.11局部变量和全局变量4.12变量的存储类别4.13变量属性小结4.14关于变量的声明和定义4.15内部函数和外部函数4.16预处理命令4.1概述在一个程序文件中可以包含若干个函数。无论是C还是C++,程序中的各项操作基本上都是由函数来实现的,程序编写者要根据需要编写一个个函数,每个函数用来实现某一功能。例:在主函数中调用其他函数。#includeiostreamusingnamespacestd;voidprintstar(void){cout″******************************″endl;}voidprint_message(void){cout″WelcometoC++!″endl;}intmain(void){printstar();print_message();printstar();return0;}函数的分类:从用户使用的角度看,函数有两种:1)系统函数2)自定义函数。从函数的形式看,函数分两类:1)无参函数。2)有参函数。1)定义无参函数的一般形式为类型标识符函数名([void]){声明部分执行语句}2)定义有参函数的一般形式为类型标识符函数名(形式参数表列){声明部分执行语句}4.2定义函数的一般形式类型标识符指定函数返回值的类型。函数名是唯一标识一个函数的名字,命名规则同变量。同一程序中不允许出现函数重名doublecylinder(doubler,doubleh)//函数首部{//函数体,写在一对大括号内*/doubleresult;result=3.1415926*r*r*h;//计算圆柱体积returnresult;//返回运算结果}函数类型函数名形参表分析函数的定义在定义函数时函数名后面括号中的变量名称为形式参数,在主调函数中调用一个函数时,函数名后面括号中的参数(可以是一个表达式)称为实际参数。4.3函数参数和函数的值4.3.1形式参数和实际参数定义函数时对形式参数声明的方式:intmax(intx,inty){intz;z=xy?x:y;return(z);}例:函数调用的过程……doublecylinder(doubler,doubleh){doubleresult;result=3.1415926*r*r*h;returnresult;}intmain(void){doubleheight,radius,volume;coutEnterradiusandheight:;cinradiusheight;volume=cylinder(radius,height);coutVolume=volume;return0;}有关形参与实参的说明:(1)形参在未出现函数调用时,它们并不占内存中的存储单元,只有在发生函数调用时,才为形参分配内存单元,在调用结束后,形参占用的内存单元也被释放。(2)实参可以是常量、变量或表达式,如max(3,a+b);但要求a和b有确定的值。以便在调用函数时将实参的值赋给形参。(3)在定义函数时,必须在函数首部指定形参的类型,如:doublecylinder(doubler,doubleh)(4)实参与形参的类型应相同或赋值兼容。(5)实参变量对形参变量的数据传递是“值传递”,即单向传递,只由实参传给形参,而不能由形参传回来给实参。intmax(intx,inty){……x=10;y=15;……}intmain(){inta=2,b=3,c;c=max(a,b);……}(1)函数的返回值是通过函数中的return语句获得的。(2)函数值的类型。既然函数有返回值,这个值当然应属于某一个确定的类型。(3)如果函数值的类型和return语句中表达式的值不一致,则以函数类型为准,即函数类型决定返回值的类型。4.3.2函数的返回值函数名([实参表列])cylinder(radius,height);注意定义和调用的区别4.4函数的调用4.4.1函数调用的一般形式1.函数语句如:printstar();2.函数表达式函数出现在一个表达式中。如c=2*max(a,b);3.函数参数函数调用作为一个函数的实参。如m=max(a,max(b,c));4.4.2函数调用的方式先想想,在一个函数中调用另一个函数(即被调用函数)需要具备哪些条件呢?所谓函数声明(declare),就是在函数尚在未定义的情况下,事先将该函数的有关信息通知编译系统,以便使编译能正常进行。4.4.3对被调用函数的声明和函数原型例:对被调用的函数作声明。……intmain(){……cinab;c=add(a,b);……}floatadd(floatx,floaty)//定义add函数{floatz;z=x+y;return(z);}其实,在函数声明中也可以不写形参名,而只写形参的类型,如:floatadd(floatx,floaty);floatadd(float,float);这种函数声明称为函数原型(functionprototype)。函数原型的一般形式为(1)函数类型函数名(参数类型1,参数类型2…);(2)函数类型函数名(参数类型1参数名1,参数类型2参数名2…);C++不能嵌套定义函数,但可以嵌套调用函数,也就是说,在调用一个函数的过程中,又调用另一个函数。见图:。4.9函数的嵌套调用例:intf(intx,inty){return(y-x)*x;}main(){inta=3,b=4,c=5,d;d=f(f(3,4),f(3,5));coutd;}输出什么结果?在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归(recursive)调用。例如:intf(intx){inty,z;z=f(y);//在调用函数f的过程中,又要调用f函数return(2*z);}4.10函数的递归调用两种方式:直接和间接调用自身包含递归调用的函数称为递归函数。例:有5个人坐在一起,问第5个人多少岁?他说比第4个人大两岁。问第4个人岁数,他说比第3个人大两岁。问第3个人,又说比第2个人大两岁。问第2个人,说比第1个人大两岁。最后问第1个人,他说是10岁。请问第5个人多大?每一个人的年龄都比其前1个人的年龄大两岁。即age(5)=age(4)+2age(4)=age(3)+2age(3)=age(2)+2age(2)=age(1)+2age(1)=10可以用式子表述如下:age(n)=10(n=1)age(n)=age(n-1)+2(n1)可以看到,当n1时,求第n个人的年龄的公式是相同的。因此可以用一个函数表示上述关系。图4.11表示求第5个人年龄的过程。图4.11程序:#includeiostreamusingnamespacestd;intage(int);//函数声明intmain()//主函数{coutage(5)endl;return0;}intage(intn)//求年龄的递归函数{intc;//用c作为存放年龄的变量if(n==1)c=10;//当n=1时,年龄为10elsec=age(n-1)+2;//当n>1时,此人年龄是他前一个人的年龄加2returnc;//将年龄值带回主函数}运行结果如下:18函数调用过程如图所示。图4.12例4.11用递归方法求n!。求n!可以用递推方法,即从1开始,乘2,再乘3……一直乘到n。求n!也可以用递归方法,即5!=4!×5,而4!=3!×4,…,1!=1。可用下面的递归公式表示:n!=1(n=0,1)n·(n-1)!(n1)#includeiostreamusingnamespacestd;longfac(int);//函数声明intmain(){intn;//n为需要求阶乘的整数longy;//y为存放n!的变量cout″pleaseinputaninteger:″;//输入的提示cinn;//输入ny=fac(n);//调用fac函数以求n!coutn″!=″yendl;//输出n!的值return0;}longfac(intn)//递归函数{longf;if(n0){cout″n0,dataerror!″endlf=-1;}elseif(n==0||n==1)f=1;//0!和1!的值为1elsef=fac(n-1)*n;//n1时,进行递归调用returnf;//将f的值作为函数值返回}局部变量在函数内定义的变量(包括形参)作用范围:本函数内部定义在复合语句内的变量作用范围:复合语句内部全局变量在函数以外定义的变量,不从属于任一函数。作用范围:从定义处到源文件结束(包括各函数)4.11局部变量和全局变量floatf1(inta)//函数f1{intb,c;b、c有效a有效┆}charf2(intx,inty)//函数f2{inti,j;i、j有效x、y有效┆}intmain()//主函数{intm,n;┆{intp,q;p、q在复合语句中有效m、n有效┆}}intp=1,q=5;//全局变量全局变量p、q的作用范围floatf1(inta)//定义函数f1{intb,c;┆}charc1,c2;//全局变量全局变量c1、c2的作用范围charf2(intx,inty)//定义函数f2{inti,j;┆}main()//主函数{intm,n;┆}例:在复合语句中定义局部变量。intmain(void){inta;a=1;{/*复合语句开始*/intb=2;b=a+b;a=a+b;}/*复合语句结束*/couta;return0;}4例:全局变量定义intx;/*定义全局变量x*/intf(){intx=4;/*x为局部变量*/returnx;}intmain(void){inta=1;x=a;/*对全局变量x赋值*/a=f();/*a的值为4*/{intb=2;b=a+b;/*b的值为4*/x=x+b;/*全局变量运算*/}coutax;return0;}4,7若局部变量与全局变量同名,局部变量优先变量的有效范围称为变量的作用域(scope)。归纳起来,变量有4种不同的作用域:文件作用域(filescope)、函数作用域(functionscope)、块作用域(blockscope)和函数原型作用域(functionprototypescope)。文件作用域是全局的,其他三者是局部的。除了变量之外,任何以标识符代表的实体都有作用域,概念与变量的作用域相似。变量还有另一种属性——存储期(storageduration,也称生命期)。存储期可以分为静态存储期(staticstorageduration)和动态存储期(dynamicstorageduration)。4.12变量的存储类别4.12.1动态存储方式与静态存储方式存储空间可以分为三部分,即:(1)程序区(2)静态存储区(3)动态存储区在动态存储区中存放以下数据:①函数形式参数。②函数中的自动变量(未加static声明的局部变量,详见后面的介绍)。③函数调用时的现场保护和返回地址等。在C++中变量除了有数据类型的属性之外,还有存储类别(storageclass)的属性。存储类别指的是数据在内存中存储的方法。存储方法分为静态存储和动态存储两大类。具体包含4种:自动的(auto)、静态的(static)、寄存器的(register)和外部的(extern)。根据变量的存储类别,可以知道变量的作用域和存储期。auto变量:auto变量类型变量列表;如:intf(inti){autointa,b,c;//或inta,b,c;……}intmain(){intk=3,t;t=f(k);……}不声明的局部变量默认为auto变量。4.12.2自动变量4.12.3用static声明静态局部变量静态局部变量(函数内