C++程序设计第5章函数.ppt
第5章 函数目的与要求5.1 函数的定义和调用5.2 函数的嵌套调用和递归调用5.3 数组作函数参数(习题课)5.4 变量的存储类型5.5 内联函数5.6 具有默认参数值的函数5.7 函数的重载本章小结目的与要求通过本章学习,应理解函数的概念,熟练掌握函数的定义和调用方法,理解函数调用时实参和形参间数据的传递方法。理解函数递归的概念,初步掌握递归的使用方法,理解变量的作用域和存储类型的概念,了解内联函数与函数重载的概念。5.1 函数的定义和调用 5.1.1 函数的概念 函数概念:具备特定功能的独立程序段。函数包括标准函数和自定义函数。【例5.1】在主函数中调用标准函数sin(x)与自定义函数f(x)。通常将调用f(x)的函数(main())称为主调函数,而将f(x)称为被调函数。例程例程5.1.2 函数的定义 1函数的定义格式 函数定义的一般格式为:类型()(1)类型 (2)函数名 (3)形参表:,(4)函数体:由一系列语句组成。空函数的定义格式为:类型(void)2.函数的形参(1)函数可以没有形参,则形成无参函数。在定义无参函数时,形参表应该写成void。无参函数的定义格式为:类型(void)【例5.2】无参函数的实例。(2)C+对形参的个数没有限制。(3)必须依次说明每一个参数的数据类型。例程例程5.1.3 函数的调用 1函数调用格式函数调用的一般格式为:()其中,实参表的一般格式为:,2函数的实参 (1)实参为能求值的表达式(2)实参和形参的类型应兼容 3.函数的三种调用方式(1)函数调用语句。函数调用语句的一般格式为:(实参表);此时不要求函数有返回值,只要求函数完成一定的操作。函数调用方式 (2)函数表达式函数调用出现在一个表达式中,此时要求函数有确定的返回值以便参与表达式的运算。(3)函数参数函数调用作为一个函数的实参。【例5.3.1】用自定义函数求三个整数的最大值。4.函数返回语句return(1)格式:return;或:return();(2)作用:返回表达式的值,结束函数执行,并将控制转移到调用函数的地方继续执行。例程例程5.函数的原型说明 在C+程序中,当函数定义在前、函数调用在后时,程序能被正确编译执行。而当函数调用在前、函数定义在后时,则应在主调函数中增加对被调函数的原型说明。(1)函数原型说明的格式:();或:();(2)函数原型说明的作用:告诉编译程序,函数返回值的类型、参数个数和各参数类型。【例5.3.2】用自定义函数求三个整数的最大值。例程例程5.1.4实参与形参的数据传送 在C+中,实参和形参间数据传送的方式有三种:值传送、传地址和引用传送。先介绍值传送方式。值传送过程:(1)调用函数时,先为形参分配存储单元,后将实参值传递给形参;(2)函数执行过程中均为形参参与运算;(3)函数调用后,形参所对应的存储单元被释放,实参保持原来的值不变。【例5.4】定义变量交换函数swap(),将两个整型变量交换数据后输出。例程例程5.2 函数的嵌套调用和递归调用5.2.1函数的嵌套调用(1)函数不允许嵌套定义:即不允许在函数内再定义函数。(2)函数可以嵌套调用:即在被调用函数体内又调用另一个函数。【例5.5】编写C+程序,求组合数的值。main函数a函数b函数调用a函数调用b函数结束返回返回函数的嵌套调用例程例程5.2.2 函数的递归调用 函数递归调用定义:在函数体中出现直接或间接地调用该函数本身的调用关系称为函数的递归调用。1.直接递归:在函数f的定义过程中出现调用函数f。2.间接递归:在函数f1的定义过程中调用函数f2,而在函数f2的定义过程中又调用了f1函数。函数的直接递归调用f函数调用f函数f1函数f2函数调用f2函数调用f1函数函数的间接递回调用递归举例 【例5.6】用递归法求n!。分析:用递归方法求n!的公式为:1 n=0 n!=1 n=1 n*(n-1)!n1 在利用递归求值时,必须注意三点:(1)递归公式:n*(n-1)!(2)递归结束条件:n=0或1(3)递归约束条件:n0例程例程递归 3.递归两个阶段 (1)递归阶段:将原问题不断分解为新的子问题,逐渐从未知向已知推进,最终达到已知的条件,即递归结束条件,此时递归阶段结束。(2)回推阶段:从已知的条件出发,按照递归的逆方向,逐一求值回推,最后达到递归的开始处,结束回推阶段,从而完成整个递归调用过程。【例5.7】用递归法求裴波那契数列的前20个数,要求每行输出4个数。例程例程5.3 数组作函数参数(习题课)5.3.1 数组元素作函数实参 传送方式:值传送。【例5.8】定义显示变量值函数print(),用数组元素作函数实参,输出数组元素值。【例5.9】定义判素数的函数prime(),用数组元素作函数实参,判断数组各元素值是否为素数。若不是素则清0。最后输出数组中的素数。例程例程例程例程5.3.2 数组名作函数参数 1.一维数组作为函数参数(1)函数定义格式(长度,)函数体说明:对于一维数组而言,函数定义中的数组长度可省略。(2)函数调用格式函数名(数组名,)注意:实参数组名后不能加“”。一维数组作为函数参数 (3)实参与形参的传送方式:传地址当数组作为函数参数,在调用函数时,系统并没有为形参数组重新分配内存空间,而是将实参数组的首地址传送给形参数组,使形参数组与实参数组占用相同的内存空间,所以对形参数组的修改就是对实参数组的修改。这种参数的传送方式称为传地址。【例5.10】编写判素数的函数,要求用数组作为函数参数,函数功能是将数组中所有非素数的元素清0。在主函数中调用判素数的函数,并输出所有素数。例程例程一维数组作为函数参数(4)说明 用数组名作函数参数时,应该在主调函数和被调函数中分别定义数组;实参数组和形参数组的类型应一致;实参数组和形参数组的大小可以一致也可以不一致。形参数组可不指定大小,在定义形参数组时,在数组名后面跟一个空的方括号,为了在被调函数中处理数组元素的需要,可以另设一个参数,传递数组元素的个数。一维数组作为函数参数【例5.11】编写程序计算某班学生的平均成绩、最高分,并对学生成绩用擂台法按升序排序。要求用函数完成上述工作,编写如下函数:(1)求平均分的函数average();(2)求最高分的函数maximin();(3)对学生成绩升序排序的函数sort();在主函数中定义一维数组并输入10个学生的成绩。调用上述函数计算并输出班级的平均成绩及最高分,并输出排序后的学生成绩。例程例程2.二维数组作为函数参数(1)函数定义格式(行长度列长度,)函数体 在函数定义时,可以明确指定二维数组的行数和列数,也可以不指定行数,但必须指定二维数组的列数。(2)函数调用格式函数名(数组名,)(3)参数传送方式为传地址。【例5.12】找出3行4列的二维数组中的最大值。例程例程5.4 变量的存储类型5.4.1 作用域 作用域是变量在程序中可引用的区域。1.块作用域 (1)块 块是用花括号括起来的一部分程序。(2)块作用域 在块内说明的变量具有块作用域,其作用域:从变量说明处到块的结束处(即块的右花括号处)。【例5.13】块作用域示例。【例5.14】同名变量的作用域示例。C+语言规定:当程序块嵌套时,如果外层块中的变量与内层块中的变量同名,则在内层块执行时,外层块中的同名变量不起作用,即局部优先。例程例程例程例程作用域2.文件作用域 在函数外定义的变量(或用extern说明的变量)具有文件作用域。其作用域:从函数外定义变量的位置开始到该源程序文件结束(可用extern进行扩展)。3.函数原型作用域 在函数原型的参数表中说明的变量具有函数原型作用。其作用域:从变量说明处开始到函数原型说明结束。4.函数作用域 函数作用域是指在函数内定义的标识符在其定义的函数内均有效,即不论在函数内的任一地方定义,均可以引用这种标识符。在C+语言中,只有标号与在函数开始处定义的变量具有函数作用域,可在整个函数内引用。5.4.2 局部变量与全局变量 1.局部变量 在函数或块内定义的变量称为局部变量,具有块作用域。2.全局变量 定义在函数外的变量称为全局变量,具有文件作用域。注意:当具有块作用域的局部变量与具有文件作用域的全局变量同名时,与局部变量同名的全局变量不起作用,即局部变量优先。但与块嵌套不同的是,在块作用域内可通过作用域运算符“:”来引用与局部变量同名的全局变量。【例5.15】全局变量、局部变量与块内局部变量示例。例程例程5.4.3动态与静态变量 1存储空间分类 (1)程序区:存放可执行程序的程序代码;(2)静态存储区:存放静态变量;(3)动态存储区:存放动态变量。动态存储区:存放动态变量 静态存储区:存放静态变量 程序存储区:存放程序 C+程序占用的存储空间 2.动态变量:在程序执行过程中分配存储空间的变量。3.静态变量:程序开始执行时就分配存储空间的变量。5.4.4 变量的存储类型 1.自动类型(auto)用自动类型关键词auto说明的变量称为自动变量。(1)特性:自动变量是动态局部变量,具有块作用域,存放在动态存储区。(2)定义格式:auto;【例5.16】使用自动变量的示例。例程例程2.寄存器类型(register)用寄存器类型关键词register说明的变量称为寄存器变量。(1)特性:寄存器变量是动态局部变量,具有块作用域,存放在CPU的寄存器或动态存储区;(2)定义格式:register;【例5.17】使用寄存器变量的示例。例程例程3.静态类型(static)用静态类型关键词static说明的变量称为静态变量。定义格式:static;(1)局部静态变量 定义在函数内的静态变量称为静态局部变量。【例5.18】用自动变量与静态局部变量求三个整数的和。(2)静态全局变量 定义在函数外或用extern说明的静态变量称为静态全局变量。【例5.19】找出两个整数中较大的一个数。例程例程例程例程4.外部类型(extern)用外部类型关键词extern说明的全局变量称为外部变量。(1)特性:外部变量为静态全局变量;(2)定义格式:extern;【例5.20】在同一文件中定义全局变量为外部变量的示例,求两个整数的最大值。【例5.21】将另一文件中的全局变量定义为本文件外部变量的示例,求两个整数的最大值。例程例程例程例程5.5 内联函数所谓内联函数,就是在编译时把函数的函数体直接插入到调用处,而不是在执行中发生调用函数时的控制转移。这样可以降低程序运行时间,提高了程序的执行效率。内联函数定义格式:inline()【例5.22】用内联函数实现求两个整数的和。说明:(1)内联函数的函数体内一般不能含有循环、switch分支和复杂嵌套的if语句。(2)内联函数的实质是用存储空间(使用更多的存储空间)来换取执行时间(减少执行的时间),即可加速程序的执行。例程例程5.6 具有默认参数值的函数 在C+中定义函数时,允许给形参指定默认值,即形参表可写成如下形 式:=,=其中表达式必须有确定的默认值。在调用函数时,若明确给出了实参的值,则使用相应实参的值;若没有给出相应实参的值,则使用默认的值。【例5.23】编写具有默认参数值的函数求三个整数的和。(1)具有默认参数值函数应先定义后调用。(2)在定义函数时,具有默认值的参数必须位于参数表的最右边。(3)函数调用时,若省略某个实参而使形参采用默认值,则其后的实参都应省略。例程例程5.7 函数的重载 同一个函数名或同一个运算符,根据不同的对象可以完成不同的功能和运算,这种特性称为重载性。所谓函数的重载是指用重名函数完成不同功能的函数运算。当然这种函数的定义必须符合函数重载的规定。【例5.24】重载求和的函数,分别求两个整数、两个实数和两个双精度实数的和。注意:(1)定义的重载函数的形参必须不同,即:或者参数个数不同或者数据类型不同。(2)如果函数名相同,形参的个数和类型均相同,但函数的返回值不同,则不能定义为重载函数。例程例程本章小结 1.函数的定义和调用函数可分成系统提供的标准库函数和用户自定义函数。标准库函数可以直接使用,而用户自定义函数则必须先定义后使用。(1)函数定义格式 类型()函数体(2)函数调用格式 ()函数调用的方式通常有三种,即:函数调用语句、函数表达式、函数参数。2.实参和形参间数据的传递(1)值传送当函数的形参为变量时,其实参为常量、变量、数组元素与表达式。调用函数时,系统给形参分配存储单元,并将实参的值传递给对应的形参;在函数执行过程中均为形参参与运算;函数返回时,形参值的改变不影响实参,实参保持原来的值。(2)传地址 当函数的形参为数组时,其实参只能是相同类型的数组名(不能加“”号)。在C+中,规定数组名表示数组的首地址,因此在调用函数时,系统将实参数组的首地址传递给形参数组,使两个数组占用相同的内存空间。在执行函数过程中,形参数组中各元素值的变化会使实参数组元素的值同时发生变化。3.函数的返回值 函数可用return语句返回一个值给主调函数。return语句的格式是:return;或:return();此时函数的类型应与与表达式的类型一致。当函数没用return语句返回值时,函数类型应为void。4.函数的原型说明在C+程序中,当函数调用在前、函数定义在后时,则应在调用前增加对被调函数的原型说明。函数原型说明的一般格式为:();或:();在函数原型说明中,可只写形参的数据类型,而省略形参名。5.函数的嵌套调用和递归调用(1)函数的嵌套调用函数的嵌套调用是在调用一个函数的过程中,在该函数的函数体内又调用另一个函数。(2)函数的递归调用 函数的递归调用是在一个函数定义的函数体中又出现直接或间接地调用该函数本身。利用递归方法解决问题时,必须注意三点:递归的公式、递归的结束条件、递归的限制条件。6.变量的作用域和存储类型 (1)作用域 作用域是变量在程序中可引用的区域。作用域共分成块、文件、函数原型、函数、类作用域五种。前四种作用域总结范围如下:块作用域:从块内变量定义开始到块结束。文件作用域:从函数外变量定义开始到文件结束(可用extern进行扩展)。函数原型作用域:从原型变量定义开始到函数原型说明结束。函数作用域:从函数开始到函数结束。(2)局部与全局变量 局部变量是在函数内部定义或在块内定义的变量。局部变量都具有块作用域。全局变量是在函数外定义的变量。全局变量具有文件作用域。当局部变量与全局变量同名时,局部变量优先。在块作用域内可通过作用域运算符“:”来引用与局部变量同名的全局变量。(3)静态与动态变量 动态变量是在程序的执行过程中为其分配内存的变量。静态变量是在程序开始执行时就为其分配内存,直到程序执行结束时,才收回内存的变量。(4)C+存储区 C+程序在内存中占用的存储空间可以分为三个部分:程序区、静态存储区和动态存储区。动态变量存储在内存中的动态存储区;静态变量存储在内存中的静态存储区。(5)变量的存储类型 在C+中,变量的存储类型分为四种:自动类型(auto)、寄存器类型(register)、静态类型(static)、外部类型(extern)。自动类型、寄存器类型属于动态存储类型;静态类型、外部类型属于静态存储类型。变量存储类型表 变量类型 全局、局部变量 作用域 静、动态变量 存储区 自动变量局部变量块作用域动态变量动态存储寄存器变量局部变量块作用域动态变量CPU寄存器静态局部变量局部变量块作用域静态变量静态存储区静态全局变量全局变量文件作用域静态变量静态存储区外部变量全局变量文件作用域静态变量静态存储区7.内联函数 在类型前加关键字inline定义的函数称为内联函数,定义格式为:inline()函数体内联函数的实质就是在编译时把函数的函数体直接插入到调用处。其目的是降低系统的开销,提高程序的执行效率。8.具有默认参数值的函数 给形参指定默认值的函数称为具有默认参数值的函数。指定默认值的形参必须写在形参表的右边。在调用函数时,若明确给出了实参的值,则使用相应实参的值;若没有给出相应实参的值,则使用默认的值。9.函数的重载 一个函数名或一个运算符,根据不同的对象可以完成不同的功能和运算,这种特性称为重载性。函数的重载是指用重名函数完成不同功能的函数运算。重载函数形参必须不同,即或者参数个数不同或者数据类型不同。例5.1#include#includefloatf(floatx)floaty;y=5*x*x+3*x+1;returny;voidmain(void)floatz1,z2,x=1;z1=sin(x);z2=f(x);coutsin(x)=z1endl;coutf(x)=z2endl;程序运行结果:sin(x)=0.841471f(x)=9返回返回例5.2#includevoidprint(void)couty)z=x;elsez=y;returnz;voidmain(void)inta,b,c,m;coutabc;coutmax(b,c)=max(b,c)endl;m=max(a,max(b,c);coutmax(a,b,c)=mendl;程序执行后,提示用户输入:Inputa,b,c:985max(b,c)=8max(a,b,c)=9返回返回#includevoidmain(void)inta,b,c,m;intmax(intx,inty);coutabc;coutmax(b,c)=max(b,c)endl;m=max(a,max(b,c);coutmax(a,b,c)=my)z=x;elsez=y;returnz;返回返回例5.4#includevoidswap(intx,inty)inttemp;temp=x;x=y;y=temp;voidmain(void)inta=3,b=4;couta=atb=bendl;swap(a,b);couta=atb=bendl;执行该程序后输出:a=3b=4a=3b=4 例5.4说明3实参a4实参b3形参x44形参y3形参交换前形参交换后实参与形参的值传送返回返回例5.5(1)#includefloatfac(intk)/定义计算K阶乘的函数fac()inti;floatt=1.0;for(i=1;i=k;i+)t=t*i;return(t);floatcmn(intm1,intn1)/定义计算组合数的函数cmn()floatp;p=fac(m1)/(fac(n1)*fac(m1-n1);/调用求阶乘函数fac()return(p);例5.5(2)voidmain(void)floats;intm,n;coutmn;s=cmn(m,n);/调用计算组合数的函数cmn()coutcmn=sendl;程序执行后提示:Inputm,n:52cmn=10返回返回例5.6#includelongintfac(intk)longintf;if(k=1|k=0)f=1;elsef=k*fac(k-1);return(f);voidmain(void)intn;longintt;coutn;t=fac(n);coutn!=tendl;程序执行后提示:Inputn:55!=120返回返回例5.7(1)#include#includelongintfib(intn)longintf;if(n=1|n=2)f=1;elsef=fib(n-1)+fib(n-2);return(f);voidmain(void)inti;for(i=1;i=20;i+)coutsetw(10)fib(i);if(i%4=0)coutendl;coutendl;例5.7(2)程序执行后输出:11235813213455891442333776109871597258441816765返回返回例5.8#includevoidprint(intx)coutxt;voidmain(void)inta5=3,7,9,12,92;for(inti=0;i5;i+)print(ai);程序执行后输出:3791292返回返回例5.9(1)#include#includeintprime(intb)inti;for(i=2;i=sqrt(b);i+)if(b%i=0)return0;return1;例5.9(2)voidmain(void)inti;inta10=2,3,4,5,6,7,8,9,10,11;for(i=0;i10;i+)if(prime(ai)=0)ai=0;for(i=0;i10;i+)if(ai!=0)coutait;coutendl;程序运行后输出:235711返回返回例5.10#include#includevoidprime(intb10)inti,j;for(i=0;i10;i+)for(j=2;j=sqrt(bi);j+)if(bi%j=0)bi=0;voidmain(void)inti;inta10=2,3,4,5,6,7,8,9,10,11;prime(a);for(i=0;i10;i+)if(ai!=0)coutait;coutendl;程序运行后输出:235711 返回返回例5.11(1)#includefloataverage(floata,intn)floatsum=0;for(inti=0;in;i+)sum+=ai;returnsum/n;floatmaximin(floata,intn)floatmax=a0;for(inti=0;imax)max=ai;returnmax;例5.11(2)voidsort(floata,intn)inti,j,k,t;for(i=0;in-1;i+)k=i;for(j=i+1;jaj)k=j;if(ki)t=ak;ak=ai;ai=t;例5.11(3)voidmain(void)floats10,ave,max;inti;coutinput10score:;for(i=0;isi;ave=average(s,10);max=maximin(s,10);sort(s,10);coutave=aveendl;coutmax=maxendl;coutsort:;for(i=0;i10;i+)coutsit;coutendl;例5.11(4)程序执行后提示:Input10score:908070 50657183 959745ave=74.6max=97sort:45506570718083909597返回返回例5.12(1)#includevoidinputarray(intarray34)inti,j;coutInputarray34:endl;for(i=0;i3;i+)for(j=0;jarrayij;例5.12(2)intmaxvalue(intarray4,intn)/定义二维形参数组时,inti,j,max;/行长度可省略,max=array00;/但列长度不能省略for(i=0;in;i+)for(j=0;jmax)max=arrayij;returnmax;例5.12(3)voidmain(void)inta34,m;inputarray(a);/实参为数组名am=maxvalue(a,3);/实参为数组名a及行长度3coutMaxvalueis:mendl;程序执行后提示:Inputarray341538679150127792113980Maxvalueis80返回返回例5.13#includevoidmain(void)inti=1,j=2,k=3;coutitjtkendl;inta=4,b=5;k=a+b;coutatbtkendl;coutitjtkendl;程序执行后输出123459129返回返回例5.14#includevoidmain(void)inti=1,j=2,k=3;coutitjtkendl;inti=4,j=5;k=i+j;coutitjtkendl;coutitjtkendl;程序执行后输出12324591 29返回返回例5.15#includeinta=10;voidmain(void)inta=20;a=a+:a;inta=50;:a=:a+a;coutat:aendl;执行程序后,其输出结果为:3060返回返回例5.16#includevoidmain(void)intx=5,y=10;for(intk=1;k=2;k+)autointm=0,n=0;m=m+1;n=n+x+y;coutm=mtn=nendl;执行程序后,其输出结果为:m=1n=15m=1n=15 返回返回例5.17#includevoidmain(void)intx=5,y=10;for(intk=1;k=2;k+)registerintm=0,n=0;m=m+1;n=n+x+y;coutm=mtn=nendl;执行程序后,其输出结果为:m=1n=15m=1n=15返回返回例5.18#includevoidf(intx,inty)intm=0;staticintn=0;m=m+x+y;n=n+x+y;coutm=mtn=nendl;执行程序后,其输出结果为:m=15n=15m=15n=30m=15n=45 voidmain(void)inti=5,j=10,k;for(k=1;k=3;k+)f(i,j);返回返回例5.19#includestaticinta=6,b=5;intmax(intx,inty)coutatby?x:y;voidmain(void)intc;c=max(a,b);coutmax=cendl;程序执行后输出:65max=6返回返回例5.20#includeexterninta,b;intmax(intx,inty)coutatby?x:y;inta=6,b=5;voidmain(void)intc;c=max(a,b);coutmax=cy?x:y;inta=100,b=7;第二个文件内容如下:/*文件名:exemple5_21.cpp*/#includeexterninta,b;externintmax(intx,inty);voidmain(void)intc;c=max(a,b);coutmax=cendl;程序运行结果如下:max=100返回返回例5.22#includeinlineintsum(intx,inty)return(x+y);voidmain(void)inta,b;coutab;coutsum=sum(a,b)endl;程序执行后提示:Inputa,b:25sum=7例5.22(预编译处理后程序)#includevoidmain(void)inta,b,m;coutab;coutsum=a+bendl;返回返回例5.23#includeintadd(intx=5,inty=6,intz=7)returnx+y+z;voidmain(void)ints1,s2,s3;s1=add(10,20,30);couts1=s1endl;s2=add(10,20);couts2=s2endl;s3=add();couts3=s3endl;程序执行后输出:s1=60s2=37s3=18返回返回例5.24(1)#includeintadd(intx,inty)/求两个整数和的函数add()retunx+y;floatadd(floatx,floaty)/求两个实数和的函数add()returnx+y;doubleadd(doublex,doubley)/求两个双精度数和的函数add()returnx+y;例5.24(2)voidmain(void)inta,b;floatm,n;doubleu,v;coutab;couta+b=add(a,b)endl;coutmn;coutm+n=add(m,n)end;lcoutuv;coutu+v=add(u,v)endl;例5.24(3)程序执行后提示:Input2integers:232+3=5Input2floatnumbers:2.53.52.5+3.5=6Input2doublenumbers:2.555553.555552.55555+3.55555=6.1111返回返回