1、单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二层,第三层,第四层,第五层,*,*,*,第,6,章自定义函数,6.3,、自定义函数,一、自定义函数的一般形式,定义函数的一般格式为:,函数值类型说明 函数名,(,参数说明表,),函数体,其中:,第一行称为,函数头,3/3/2026,1,函数值类型说明,:,是指函数返回值的数据类型,.,如果某一函数不需要返回值,则函数值类型说明可选用,void.,函数名,:,为一个标识符,.,参数说明表,格式为:,类型 参数,1,类型 参数,2,类型 参数,n,用来说明完成函数功能所需的必要参数,这些参数在设计函数时经常还无具体值,因此这些参数也称为形式
2、参数,(,形参,),或虚拟参数,.,此处定义的参数,(,变量,),仅在本函数内有效,.,3/3/2026,2,函数体,:,本身是一个分程序,由语句和其他分程序组成。,分程序,:,说明语句部分,执行语句部分,函数体内可随时声明一个变量,但同一函数内不能重复声明同一变量。,3/3/2026,3,函数返回值,:,函数的返回值也称函数值,返回的不是函数本身,而是一个值,是通过函数体内部的,return,语句提供。,return,语句的格式为:,return(,表达式,);,return,语句的功能是将表达式的值作为函数值返回,,(),可有可无。,return,语句提供的函数值的类型应与函数声明中的函数
3、值类型一致,否则会作类型的强制转换,如果数据类型不相容,则编译时会出错。,3/3/2026,4,如:,double add(double x,double y),return x+y;,又如:,int,fa(int,n)/,求,n!,int,j,y=1;,for(j=1;jb)?a:b;,(2),不获取参数但返回值,double,geti,(),int,x;,cin,x;,return x;,3/3/2026,6,(3),获取参数但不返回值,void delay(long a),for(int,j=1;j=a;j+;,(4),不获取参数也不返回值,void message(),cout,Thi
4、s is a message.n,无返回值的函数也可以使用,return,,,但不能返回值。,void print(),for(int,i=0;i60;i+),cout,*;,cout,endl,;return;,3/3/2026,7,C+,不允许函数定义嵌套,即在函数定义中再定义一个函数是非法的。,如:,void main(),void,func,()/,非法,3/3/2026,8,函数类型声明可以省略,此时,:,若函数有返回值,则返回值强制为整型;,若函数无返回值,(,无,return,或,return,后不跟任何返回值,),,则此函数为,void,型。,#include,using na
5、mespace std;,max(double x,double y)/,int,型返回值,return x+y;,void main(),double x,y;,cin,xy;,cout,max(x,y),endl,;,/,输入,3.5 4.4,输出,7,3/3/2026,9,#include,using namespace std;,max(double x,double y)/void,型返回值,double z=x+y;,return;,void main(),double x,y;,cin,xy;,cout,max(x,y),endl,;,/,输出内容错,未实现加法功能,3/3/20
6、26,10,二,、函数,调用,在,C+,程序中,除了,main(),函数外,其它函数都不能独立地在程序中存在,任一函数的执行都是通过在,main(),函数中直接或间接地调用来执行,调用一个函数就是执行该函数的函数体的过程。,函数调用的一般形式为:,函数名,(,实参表,),实参表是调用函数时所提供的实在参数值,这些参数值可以是常量、已赋值的变量或可求值的表达式。,3/3/2026,11,实参表中多于一个实参时,用逗号分开,它们的类型、个数和顺序应与函数的参数表中的参数个数和类型一一对应。,函数的调用既可以出现在表达式可出现的任何地方,也可以以函数调用语句(后加分号)的形式独立出现,调用后的结果是
7、函数的返回值,其类型与定义函数时的函数值类型相同。,函数不允许嵌套声明,但可以嵌套调用,也可以递归调用,即函数直接或间接调用自身。,3/3/2026,12,函数调用的执行过程:,main(),调,fun(),结束,fun(),返回,保存:,返回地址,当前现场,恢复:,主调程序现场,返回地址,3/3/2026,13,例,6.1,、调用函数,add(),求,5a+4b,#include,using namespace std;,double add(double x,double y),x=5*x;,y=4*y;,cout,x=xty=yab;,c=add(a,b);,cout,c=ct;,cou
8、t,a=atb=b,endl,;,3/3/2026,15,例,6.2,、计算,1!+2!+3!+,+m!,程序一,:,#include,using namespace std;,int,fa(int,n),for(int,fac,=1,i=1;i=n;i+),fac,=,fac,*i;,return,fac,;,3/3/2026,16,void main(),const,int,m=8;,for(int,sum=0,i=1;i=m;i+),sum=,sum+fa(i,);,cout,m=m,endl,;,cout,s=sum,endl,;,3/3/2026,17,程序二,(,嵌套调用,),:,
9、include,using namespace std;,int,fa(int,n),for(int,fac,=1,i=1;i=n;i+),fac,=,fac,*i;,return,fac,;,3/3/2026,18,int,sum(int,m),for(int,su,=0,i=1;i=m;i+),su,=,su+fa(i,);,return,su,;,void main(),const,int,m=8;,cout,m=m,endl,;,cout,s=sum(m),endl,;,3/3/2026,19,例,6.3,、,编写程序求,的值,其中,arctan,用如下形式的级数计算:,直到级数某项
10、绝对值不大于,10,-15,为止;,和,x,均为,double,型。,3/3/2026,20,#include,using namespace std;,void main(),double a,b;,double,arctan(double,x);,a=16.0*arctan(1/5.0);,b=4.0*arctan(1/239.0);,cout,PI=a-b1e-15),f=e/i;,r=(i%4=1)?r+f:r-f ;,e=e*,sqr,;i+=2;,return r;,运行结果:,PI=3.14159,3/3/2026,22,例,6.4,、寻找并输出,11999,之间的数,m,,,它
11、满足,m,、,m,2,和,m,3,均为回文数。,分析:,10,取余的方法,从最低位开始,依次取出该数的各位数字。按反序重新构成新的数,比较与原数是否相等,若相等,则原数是回文数。,#include,using namespace std;,void main(void),bool,symm(long,n);long m;,3/3/2026,23,for(m=11;m1000;m+),if(,symm(m)&symm(m,*,m)&symm(m,*m*m),cout,m=m m*m=m*m,m*m*m=m*m*m,endl,;,bool,symm(long,n),long i=n,m=0;,wh
12、ile(i),m=m*10+i%10;i=i/10 ;,return(m=n);,3/3/2026,24,三,、,函数原型,函数和变量一样,在使用之前要先说明。,函数的定义可视为对函数的声明,如果函数的定义放在调用之前,则可不用另外声明;如果函数的定义放在调用之后,则必须对函数先作声明,否则将在编译时出错。,函数声明的一般形式为:,函数返回值类型说明 函数名,(,参数表,);,其中各部分的意义与函数定义相同。,3/3/2026,25,C+,中,函数声明就是,函数原型,。,函数原型跟函数定义时的区别:,函数原型没有函数体且用分号结束,类似变量声明,函数原型中的参数表可以只说明形参类型,而无形参本
13、身。,有了函数原型,则只要将其放在函数的调用之前,则即使函数的定义放在调用之后,也不会引起编译失败。,函数原型和函数定义在返回类型、函数名和参数表上必须一致,否则会产生编译错误;如果函数原型不正确,编译器也会及时报告错误。,3/3/2026,26,例,6.5,、改写例,6.2,、程序一,#include,using namespace std;,void main(),const,int,m=8;,int,fa(int,n);/,int,fa(int,);,for(int,sum=0,i=1;i=m;i+),sum=,sum+fa(i,);,3/3/2026,27,cout,m=m,endl,
14、cout,s=sum,endl,;,int,fa(int,n),for(int,fac,=1,i=1;i=n;i+),fac,=,fac,*i;,return,fac,;,3/3/2026,28,四,、变量的作用域和生存期,变量的作用域,:指一个变量在程序的哪一部分可以使用。,变量的生存期,:指一个变量在计算机内存中能保留的时间。,1,、局部变量和全局变量,根据作用域的不同,可将程序中的变量分为局部变量和全局变量。,局部变量,是在函数或分程序中说明的变量,只能在本函数或分程序的范围内使用。,局部变量的类型修饰是,auto,通常都将其省略,3/3/2026,29,例,6.6,、分程序中说明的
15、变量只能在本分程序的范围内使用。,#include,using namespace std;,void main(),int,i=1;,int,i=2,j=3;,cout,i=i,j=j,endl,;,cout,i=i;/,cout,j=j;,cout,endl,;,3/3/2026,30,例,6.7,、函数中说明的变量只能在本函数内使用,#include,using namespace std;,int,add();,void main(),int,a=2,b=3,c;,c=add();,cout,c=c,endl,;,int,add()/,错误,:a,和,b,未定义,int,x,y;,x=
16、a,y=b;return x+y;,3/3/2026,31,例,6.8,、函数中说明的变量只能在本函数内使用,#include,using namespace std;,int,add(int,int,);,int,add(int,x,int,y),return x+y;,void main(),int,a=2,b=3,c;,c=add(a,b);,cout,c=c,endl,;,cout,xy,endl,;/,错误,:x,和,y,未定义,3/3/2026,32,一个函数可以为局部变量定义任何名字,而不用担心其它函数使用过同样的名字。在函数开始运行时,局部变量分配存储空间,函数退出时,局部变量
17、随之消失。,例如:两个函数中都定义了变量,n,,,但不同,void main(),int,n;,void,func,(),int,n;,局部变量在定义时若未没有初始化,则其值是不确定。,3/3/2026,33,全局变量,说明于所有函数之外,可以为本源程序文件中位于该全局变量说明之后的所有函数共同使用。全局变量通常在程序顶部定义,也可以在程序中间的任何地方定义,但要定义在任何函数之外。,全局变量在定义时,若未对它设置初始值(作初始化),则会由系统自动初始化为,0,值。,若某函数修改了一全局变量的值,则其它所有函数再用到该全局变量时,取得的值是修改后的值。,3/3/2026,34,全局变量一旦定义
18、后,就可在其后的所有程序中使用,但全局变量定义之前的所有函数定义,将不会认识该变量。如右上程序,编译出错。,int,n=5;/,全局变量,void main(),int,m=n;,void,func,(),int,s;,n=s;,void main(),int,m=n;,int,n=5;/,全局变量,void,func,(),int,s=3;,n=s;,3/3/2026,35,当函数内有与全局变量同名的局部变量时,则局部变量起作用,全局变量被屏蔽,.,例,6.9,、,#include,using namespace std;,int,a=1,b=2;/,定义全局变量,void func1(in
19、t,a,int,b),int,t;t=a;a=b;b=t;,int,func2(int,x,int,y),int,a;a=x+y;return a;,3/3/2026,36,void main(),func1(a,b);,cout,调用,func1,后,a=,a,b=b,endl,;,func2(a,b);,cout,调用,func2,后,a=,a,b=b,endl,;,3/3/2026,37,例,6.10,、编写一交换两个整型变量值的函数,并写一主函数测试。,程序:,#include,using namespace std;,int,a=1,b=2;/,定义全局变量,void swap(),
20、int,t;,t=a,a=b,b=t;,3/3/2026,38,void main(),cout,交换前,:a=a,b=b,endl,;,swap();,cout,交换后,:a=a,b=b,endl,;,输出交换前,:a=1,,,b=2,交换后,:a=2,,,b=1/,交换成功,3/3/2026,39,2,、自动变量和静态变量,自动变量,:,随函数调用或分程序运行而生成,随函数调用结束或分程序运行结束而消失的变量。,系统不会对自动变量自动作初始化,如果程序中不对它作显式初始化,那么,其值是不确定的。,对自动数组也和自动变量一样。,但数组在定义时若对其中某些元素赋了初值,则其它未赋初值的元素将自
21、动设置为,0.,3/3/2026,40,静态变量:自定义开始生成,直到整个程序运行结束才消失的变量。所有的全局变量都是静态变量。,静态局部变量:只能在其定义的函数或分程序中使用,函数结束时,静态局部变量不会消失,每次进入该函数或分程序时,也不会为它重新分配存储单元。只有到整个程序运行结束时,静态局部变量才会消失。,静态局部变量的定义:在局部变量定义前加上关键字,static,则定义成了静态局部变量。,如:,static,int,a=1;,static double b=1.5;,3/3/2026,41,#include,using namespace std;,void,add(int,int
22、);,void main(),int,a=2,b=3;,add(a,b);,cout,c,endl,;/,错误:,c,未定义,void,add(int,x,int,y),static,int,c;,c=x+y;,3/3/2026,42,#include,void main(),for(int,i=1;i5;i+),double a=5.5;,a=a+i;,cout,a ;,cout,endl,;,/,输出结果,6.5 7.5 8.5 9.5,静态局部变量的定义语句仅在第一次执行时有效,以后再执行时不再有效。静态局部变量在定义时,若未对它作显式初始化,则会由系统自动初始化为0值。,#,incl
23、ude,void main(),for(,int,i=1;i5;i+),static,double a=5.5;,a=a+i;,cout,a ;,cout,endl,;,/,输出结果,6.5 8.5 11.5 15.5,3/3/2026,43,例,6.12,、计算,1!+2!+3!+,+m!,#include,using namespace std;,int,fa(int,n),static,int,fac,=1;/,改为,int,fac,=1;?,fac,=,fac,*n;return,fac,;,void main(),const,int,m=8;,for(int,sum=0,i=1;i=
24、m;i+)sum=,sum+fa(i,);,cout,m=m,endl,;,cout,s=sum,endl,;,/s=46233,3/3/2026,44,五、其它类型变量,寄存器,变量,:,存放在,CPU,寄存器中的,变量。,如:,register,int,code;,外部,变量,:,其它程序文件中定义,的,变量。,如:,extern,int,fei,;,extern,声明的变量不分配存储单元。,3/3/2026,45,6.4,、,函数调用过程中的参数传递,调用函数时,一般要向被调用的函数传递一些数据值,也就是说要求给出实参表,同样被调用的函数也可能要向其调用者返回一些数据,这些数据主要是通过
25、函数的参数与函数的返回值来传递的,这一过程也即为函数间参数的传递过程。,在调用一个带有参数的函数时,就存在一个实参与形参结合方式的问题,即参数的传递方式问题,C+,中实参与形参有三种结合方式:,值调用、地址调用和引用调用。,3/3/2026,46,X,N,被调函数:,主调函数:,3,2.5,A,D=power(A,3),2.5,3,double power(double X,int,N),1,、值调用,值调用时先计算表达式的值,再赋值给形参。因此调用是将实参拷贝一个副本给形参,然后执行函数体,在执行过程中,形参可以被改变,但不会影响调用函数的实参值,即调用过程中实参向形参传值具有单向性,是单向
26、传递。,3/3/2026,47,例,6.13,、输入两个数,并输出其中较大的数,程序一:,#include,using namespace std;,void main(),int,a,b,c;,int,max(int,x,int,y);,cout,ab;c=max(a,b);,cout,max=cy),z=x;,else,z=y;,return(z);,3/3/2026,49,程序二:,#include,using namespace std;,void main(),int,a,b,c;,int,max(int,a,int,b);,cout,ab;,3/3/2026,50,c=max(a,
27、b);,cout,max=cb?a:b;,3/3/2026,51,例,6.14,、编写一交换两个整型变量值的函数,并写一主函数测试。,程序一:(交换不成功),#include,using namespace std;,void,swap(int,x,int,y),int,t;t=x,x=y,y=t;,void main(),int,a=1,b=2;,cout,交换前,:a=a,b=b,endl,;,swap(a,b);,cout,交换后,:a=a,b=b,endl,;,3/3/2026,52,程序二:(交换不成功),#include,using namespace std;,void main
28、),int,a=1,b=2;,void,swap(int,a,int,b);,cout,交换前,:a=a,b=b,endl,;,swap(a,b);,cout,交换后,:a=a,b=b,endl,;,void,swap(int,a,int,b),int,t;t=a,a=b,b=t;,3/3/2026,53,程序三:(交换不成功),#include,using namespace std;,void main(),int,a=1,b=2;,int,swap(int,a,int,b);,cout,交换前,:a=a,b=b,endl,;,swap(a,b);,cout,交换后,:a=a,b=b,e
29、ndl,;,3/3/2026,54,int,swap(int,a,int,b),int,t;t=a,a=b,b=t;return a;/return b,值调用的特点好处:减少了调用函数与被调用函数之间的数据依赖,增强了函数自身的独立性。,缺点:被调用函数向调用函数传递的数据仅有一个返回值,有时显得不够用。,3/3/2026,55,2,、地址调用,(,传址调用,),调用时给出的实参是变量的地址值,而形参一般为指针变量,这种方式通过间接访问可以改变实参变量所指向的值。,数组作为函数参数,数组作为函数参数时的函数调用实质上是地址调用,实参传送的是数组的首地址,(,数组名,),,形参可以是数组或引用
30、实参和形参共用内存中的同一个数组(存储单元相同),,对形参数组的改变会直接影响到实参数组,,因此数组元素的各种操作都可以通过调用函数来实现。,3/3/2026,56,数组作为函数参数时,一般要同时传送数组元素的个数。,例,6.15,、,将键盘输入的,10,个数按从小到大的方式排序并输出,要求排序由子函数实现,。,算法分析:可使用选择法来进行,排序,。,先将,10,个数中的最小的数与第,1,个数对调,再将后面,9,个数中的最小的数与第,2,个数对调,,,每比较一轮,找出一个未经排序的数中最小的数与相应位置的数对调。,3/3/2026,57,void sort(double a,int,n),i
31、nt,i,j;double temp;,for(i=0;in-1;i+),for(j=i+1;jaj),temp=ai;ai=aj;aj=temp;,#include,using namespace std;,#include,3/3/2026,58,void main(void),const,int,N=10;,int,i;double aN;,cout,输入需排序的,N,个数:,endl,;,for(i=0;iai;,sort(a,N);,cout,排序后的,N,个数为,:,endl,;,for(i=0;iN;i+),cout,setw(8)ai;,3/3/2026,59,例,6.16,、
32、主函数中初始化一个矩阵并将每个元素都输出,然后调用子函数,分别计算每一行的元素之和,将和直接存放在每行的第一个元素中,返回主函数之后输出各行元素的和。,程序:,#include,using namespace std;,void RowSum(int A 4,int nrow),for(int i=0;inrow;i+),for(int j=1;j4;j+),Ai0+=Aij;,3/3/2026,60,void main(void),int Table34=1,2,3,4,2,3,4,5,3,4,5,6;,for(int i=0;i3;i+),for(int,j=0;j4;j+),coutTa
33、bleij ;,coutendl;,RowSum(Table,3);,for(i=0;i3;i+),coutSum of row i,is,Tablei0endl;,3/3/2026,61,数组元素作实参,与单个变量一样,不会改变数组元素的值。如:,#include,using namespace std;,void main(),int,a2=0,1;,void,add(int,int,);,add(a0,a1);,cout,a0,endl,;,void,add(int,x,int,y),x=x+y;/,运行后输出结果仍为,0,3/3/2026,62,3.,引用调用,引用实际上是某一变量的别
34、名,要用另一个变量或对象的名字初始化它。从建立之后,引用作为目标对象的别名使用,对引用的改动实际上就是目标对象的改动。,引用是通过引用运算符,&,来声明,如:,int,i,&refi,=i;/,建立了一个对整型变量,i,的引用,建立之后,,refi,就是变量,i,的一个别名,,i,与引用,refi,代表的是同一变量,对,refi,的操作就是对变量,i,的操作。,3/3/2026,63,如:,int,i=100,refi,+=100;/,结果是变量,i,的值变成,200,引用不是值,不占存储空间,声明引用时,目标对象的存储状态不会改变。引用在声明时必须被初始化。,用引用传递函数参数,传递引用给函
35、数时,传递的是原来的变量或对象,而不是传递变量或对象的副本给函数内的形参。,3/3/2026,64,例,6.17,、编写交换两个整型变量值的函数。,#include,using namespace std;,void,swap(int&,int,void main(),int,x=1,y=2;,cout,交换前,:x=x,y=y,endl,;,swap(x,y);,cout,交换后,:x=x,y=y1/,递归公式,1!=1n=1/,终止条件,3/3/2026,66,设求阶乘的递归函数为,fa,(),则递归条件也可写成:,fa(n,)=n*fa(n-1);n1/,递归公式,fa(1)=1;n=1
36、/,终止条件,由上面条件,递归函数,fa,(),可定义成:,int,fa(int,n),if(n=1)return 1;,return n*fa(n-1);/,函数自调用,3/3/2026,67,大多数递归函数都能用非递归函数来代替,递归函数能简化程序设计,使程序易读。但系统开销很大,相应的非递归函数虽然效率高,但却比较难编程,而且相对来说可读性差。,递归函数不能无终止条件,否则造成无限递归。下面的函数无条件调用自己,造成无限递归,最终出错。,void,count(int,val,),count(val-1);/,无限递归,if(val,1)/,无法执行到此语句,cout,“,OK!,”,va
37、l,endl,;,3/3/2026,68,例,6.18,、用递归方法计算,Fibonacci,数列,#include,using namespace std;,int,fibonacci(int,n),int,fibo,;,if(n=1|n=2),fibo,=1;,else,fibo,=fibonacci(n-1)+fibonacci(n-2);,return,fibo,;,3/3/2026,69,void main(void),int,n,fibon,;,cout,n;,fibon,=,fibonacci(n,);,cout,The n,number of,fibonacci,is:,fib
38、on,;,cout,endl,;,3/3/2026,70,例,6.19,、用递归法计算从,n,个人中选择,k,个人组成一个委员会的不同组合数。,分析:,n,个人里选,k,个人的组合数,=,n-1,人中选,k,个人的组合数,+,由,n-1,人中选,k-1,人的组合数,当,n=k,或,k=0,时,组合数为,1,3/3/2026,71,#include,using namespace std;,void main(void),int,n,k;,int,comm(int,n,int,k);,cin,nk;,cout,comm(n,k,)n)return 0;,else if(n=k|k=0)return 1;,else,return comm(n-1,k)+comm(n-1,k-1);,3/3/2026,72,






