版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
高级语言程序设计张莹计算机与控制工程学院第5章函数与重载函数的说明与使用1函数的嵌套与递归2函数与运算符重载3函数与C++程序结构41函数的使用与说明【例5.1】求三次方程的根。计算三次方程x3+px+q=0的一个实根的公式为2xr=将计算公式分解为如下步骤令实数xr=A+B令实数A,B分别为实数R,S的立方根令R=-q/2+a,S=-q/2-a令a=sqrt((q/2)*(q/2)+(p/3)*(p/3)*(p/3))函数的使用与说明程序设计步骤输入方程的参数p和q,输出方程计算变量a的值计算变量R和变量S的值计算变量A和变量B的值分别对R和S求三次方根求y的三次方根的迭代公式计算Xr的值3函数的使用与说明程序代码4#include<iostream>//program5-1#include<cmath>//usefunctionsqrtusingnamespace
std;voidmain(void){
floatp,q,xr; cout<<″Inputparameters:p=″;
cin>>P;
cout<<″Inputparameters:q=″; cin>>q;
cout<<"TheEquationis:"; cout<<"x3+"<<p<<"x+"<<q<<"=0"<<endl;
floata=sqrt((q/2)*(q/2)+(p/3)*(p/3)*(p/3));
float
R,S,A,B;
constfloat
eps=1e-6;//设置精确度
R=-q/2+a;
S=-q/2-a;函数的使用和说明5
float
root,croot=R;
do{
root=croot;
croot=(2*root+R/(root*root))/3;
}while(fabs(croot-root)>eps);
A=croot;
croot=S;
do{
root=croot;
croot=(2*root+S/(root*root))/3;
}while(fabs(croot-root)>eps);
B=croot; xr
=A+B; cout<<endl<<″Therealrootoftheequationis″<<xr;}函数的使用和说明程序分析程序中包含两段类似的代码,完成相同的功能:6float
root,croot=R;do{ root=croot;
croot=(2*root+R/(root*root))/3;}while(fabs(croot-root)>eps);A=croot;croot=S;do{
root=croot;
croot=(2*root+S/(root*root))/3;}while(fabs(croot-root)>eps);B=croot;函数的使用和说明函数的引入程序中功能相同,结构相似的代码段可以用函数进行描述程序的功能相对独立,用来解决某个问题具有明显的入口和出口入口:参数出口:返回值7函数的使用和说明8函数的使用和说明声明函数cuberoot参数为待求根的变量函数cuberoot的定义如下:9floatcuberoot(floatx){//精确到小数点后6位
floatroot,croot;
constfloateps=1e-6; croot=x;
do{
root=croot;
croot=(2*root+x/(root*root))/3;
}while(fabs(croot-root)>eps);
returncroot;}函数的使用和说明函数的作用实现程序功能的模块化实现程序结构的简化实现程序代码的重用函数的应用场景包含多处功能相同的代码处理数据的类型、处理过程相同或相似代码段具有代表性或特殊含义10函数的使用和说明用函数的思想实现最简单的C++程序11#inlcude<iostream>usingnamespacestd;voidprintString();//函数原型intmain(){printString();//调用函数printString
return0;}voidpirntString(){//函数定义cout<<“Hello!”<<endl;
return;//函数返回}函数的说明与使用函数的使用步骤先说明,后调用程序中必须包含函数的说明及定义函数的说明方式函数原型函数原型必须出现在调用函数之前函数定义可以出现在程序的任何合适的地方函数定义函数定义必须出现在调用函数之前12函数的说明与使用主调函数函数体中调用其它函数被调函数被其它函数调用的函数上述概念是相对的概念,一个函数既可以调用其它函数(包括该函数自身),亦可以被其它函数调用(包括该函数自身)13从函数形式划分,可分为无参函数与有参函数两类从使用角度划分,可将函数分为:系统预定义的标准库函数(如,sin,abs等),以及由用户自定义的函数函数分类方法14函数的说明与使用函数的说明与使用标准库函数程序中可直接使用(调用)系统预定义的标准库函数,但要求在调用前使用编译预处理指令include将对应的头文件包含进来用户自定义函数由用户自定义的函数与系统预定义的标准库函数的不同点在于,自定义函数的函数名、参数个数、函数返回值类型以及函数所实现的功能等都完全由用户程序来规定(指定)15函数的说明与使用标准库函数标准输入输出流(<iostream>)setw()、setwidth()等标准文件流(<fstream>)open()、close()等标准字符串处理函数(<cstring>)strcpy()、strcmp()等标准数学函数(<cmath>)sqrt()、pow()等16函数的说明与使用用户自定义函数普通函数内联函数类的成员函数类的友元函数虚函数……17函数的说明与使用无参函数调用它们时不需要提供实际参数函数原型的一般形式<返回值类型><函数名>();函数定义的一般形式<返回值类型><函数名>(){<函数体>}通常用来实现某种特定的功能不需要进行数据的传递处理的数据通常与主调函数无关18函数的说明与使用无参函数【例5.2】定义一个函数,实现打印10个“*”的功能19voidprintStar(){
for(inti=0;i<10;i++) cout<<“*”;cout<<endl;}
函数的说明与使用有参函数(带有参数的用户自定义函数)进行调用时,必须提供所需个数的且具有相匹配数据类型的实际参数函数原型的一般形式<返回值类型><函数名>(<以逗号分割的形参类型表>);定义的一般形式<返回值类型><函数名>(<以逗号分割的形参类型及名字表>){<函数体>}通过调用处提供的不同实参值来计算出其对应的函数值、或实现某种与传递过来的那些不同实参值有关的某种功能(返回值为void)。20函数的说明与使用有参函数【例5.3】定义一个函数,控制输出“*”的个数21voidprintStar(intk){
for(inti=0;i<k;i++) cout<<“*”;cout<<endl;}
函数的使用调用说明与定义参数重载返回内联函数22函数的说明与使用函数的说明与使用23函数的说明与使用函数的说明和定义在C++程序中,函数有两种说明方式函数原型在调用函数之前说明函数原型函数定义在调用函数之前对函数进行定义24函数的说明与使用函数原型也称为函数声明用来指明函数的名称、参数以及返回值类型函数原型格式为:[<属性说明>]<返回值类型><函数名>([<参数表>]);例如intadd(inta,intb);inlinevoidswap(float&s,float&t);voidprint(char*);25函数的说明与使用函数原型属性说明:可缺省,一般可以是下面的关键字之一inline:表示该函数为内联函数static:表示该函数为静态函数virtual:表示该函数为虚函数friend:表示该函数为某类(class)的友元函数返回值类型函数处理得到的结果的数据类型函数名标识符26函数的说明与使用函数原型参数表空参数表voidprintroot();void型参数voidprintroot(void);<数据类型>[<参数>][,<数据类型>[<参数>]]*
floatcuberoot(float);floatcuberoot(floatx);27函数的说明与使用函数定义函数定义与函数原型的主要区别是它还包括函数体,其格式为[<属性说明>]<返回值类型><函数名>([<参数表>]){<函数体>}函数体复合语句即程序块,由完成函数功能所需的全部语句构成28函数的说明与使用函数的说明和定义“函数原型”的说明方式“函数定义”的说明方式29#include……usingnamespacestd;voidprintStar(int);//函数原型intmain(){printStar(10);}//调用函数voidprintStar(intk){}//函数体#include……usingnamespacestd;voidprintStar(intk){}//函数定义intmain(){printStar(10);}//调用函数函数的说明与使用函数的说明和定义两种说明方式的区别函数原型的参数表中,参数名可以省略;函数定义的参数表中,必须给出参数名(省略参数名为无名参数)函数原型的函数体,可以出现在函数调用之后;函数定义的函数体,必须出现在调用之前函数原型的参数表后面加分号“;”,函数定义的参数表后面是函数体,即花括号“{”函数定义不能出现在任何函数体中,函数原型可以出现在其它函数体中30函数的说明与使用函数的调用函数调用是已定义函数的一次实际运行,与某类型的一个变量和后文中某类的一个对象类似,函数调用是函数定义的一个“实例”在C++程序中,除main函数外,其它任一函数的执行都是通过在main函数中直接或间接地调用该函数而引发的。调用一个函数就是去执行该函数之函数体的过程31函数的说明与使用函数的调用函数调用过程32函数的说明与使用函数的调用函数调用的执行顺序根据调用语句中的函数名在整个程序中搜索同名函数定义;对实参数的参数个数,类型,顺序进行核对,判定是否与函数定义中的形参表对应一致根据参数的类型(值参数或引用参数)进行值参数的值传递或引用参数的换名运行函数体代码返回调用点,并返回所要求的函数值33函数的说明与使用函数的调用无参函数调用格式<函数名>()例如:printStar();
有参函数调用格式<函数名>(<以逗号分割的实参表>)例如:printStar(26);34函数的说明与使用函数的返回函数的返回表示函数执行结束,将执行结果(无论是否有具体的数据)返回到调用函数的地方函数返回时完成的任务把运行控制从函数体返回到函数调用点根据返回值要求,返回所需要的数据值返回值类型void数值型引用类型35函数的说明与使用函数的返回返回值类型空型(void)如果函数无值返回,应说明为void类型。未作类型说明的函数,系统认为是int类型函数,应返回一整型值值型:返回一个具有类型的值,包括int、float、char、bool等当函数要返回的值不止一个时,情况比较复杂,一般它可以以结构或类的形式,也可以以结构,数组或对象指针类型方式实现,这样的实例在后面的章节可以见到引用类型:详见第6章36函数的说明与使用函数的返回函数返回用return语句表示return语句有如下几种写法return;//函数返回值类型为空(void)return<表达式>;//与函数返回值的类型一致return
(<表达式>);//与前一种写法等价37第一种格式的return用于立即从被调函数中返回,当函数类型为void时,应使用这种格式的返回语句。当函数类型为非void型时,应使用第二或第三种格式的return语句,此两种格式的语句效果完全相同(可将第二种格式看成是第三种格式的省略形式),系统此时都将计算出表达式的值,并“携带”该值立即从被调函数中返回函数的说明与使用函数应用举例【例5.4】设f(x)=(x*x+x+1)/2-5.5求z=(f(2.5)+2*f(6))/f(4.3),并显示结果z。对任意输入的一个实数a,求出f(a)并显示分析:输入(参数):x输出(返回):函数的运算结果函数原型:doublef(double);38函数的说明与使用函数应用举例【例5.4】函数定义
doublef(doublex){
doubley; y=(x*x+x+1)/2-5.5;
returny;
//对非void类型的函数,必须有一个//return语句,由它返回函数值
}
39函数的说明与使用函数应用举例【例5.4】程序#include<iostream>usingnamespacestd;doublef(double);intmain(){
doublez,a; z=(f(2.5)+2*f(6))/f(4.3);//调用自定义函数f cout<<"z="<<z<<endl; cout<<"Inputa="; //提示用户输入
cin>>a; cout<<"f(a)="<<f(a)<<endl;//算出f(a)并输出
return0;} doublef(doublex){……}//函数f的定义40函数的说明与使用函数应用举例【例5.4】程序(不带函数原型)#include<iostream>usingnamespacestd;doublef(doublex){……}//函数f的定义intmain(){
doublez,a; z=(f(2.5)+2*f(6))/f(4.3);//调用自定义函数f cout<<"z="<<z<<endl; cout<<"Inputa="; //提示用户输入
cin>>a; cout<<"f(a)="<<f(a)<<endl;//算出f(a)并输出
return0;} 41函数的说明与使用函数应用举例【例5.4】点评f()函数体内的3行也可用如下的一行来代替
return((x*x+x+1)/2-5.5);return句括号内表达式的值,即为整个函数的返回值。return句也可使用另一格式,即可以不括起表达式:
return(x*x+x+1)/2-5.5;//OK!42①
f=(x*x+x+1)/2-5.5;不可给函数名f赋值。②
return(f);返回值类型应该是double,而非指针类型(函数名相当于一个指针)。③
f(x)=(x*x+x+1)/2-5.5;赋值号左端非变量(也即f(x)非左值)。
函数的说明与使用函数的参数C++语言的函数分为无参函数和有参函数函数的参数无参数一个参数多个参数函数参数表的写法一般写法省略参数名(无名参数)参数赋初值43函数的说明与使用函数的参数形参和实参函数说明中的参数称为形式参数(形参),函数调用中的参数称为实际参数(实参)实参表在参数个数、参数顺序、以及参数类型等方面要与被调函数的形参表之间有一个一一对应的相互匹配关系编译器将根据参数的顺序,来逐一实现实参与对应形参的“结合”,而后执行一遍函数体(而完成本次的函数调用)44函数的说明与使用函数的参数无名参数函数定义中,只有类型,没有名称的参数 intf(inta,intb){returna+b*b;} intf(inta,intb,int){returna*a+b;}两个不同的函数同名,但由于第二个函数包含一无名参数,使得在调用时能够被区分,f(x,y)是第一个函数的调用,f(x,y,0)是第二个函数的调用45函数的说明与使用函数的参数可缺省参数(参数的默认值)允许在函数定义处为连续若干个参数设置默认值(也称缺省值)若调用处缺省了某个或某些实参的情况下,系统将自动使用那些在函数定义处给定的参数默认值例如,在定义函数func时为其三个参数设置默认值:46#include<iostream>usingnamespacestd;voidfunc(inta=11,intb=22,intc=33){
//为参数a、b、c设置了默认值11、22与33
cout<<"a="<<a<<",b="<<b<<",c="<<c<<endl;}函数的说明与使用47intmain(){func();//调用时缺省了3个实参,将使用 //定义处给定的那3个相对应的参数默认值
func(55);//调用时缺省了后2个实参,将使用 //定义处给定的那后2个对应参数默认值
func(77,99);//调用时缺省了最后1个实参,将使用 //定义处给定的那最后1个参数默认值
func(8,88,888);//调用时没缺省任一个实参, //系统将不使用定义处给定的任一个参数默认值 return0;}函数的说明与使用运行结果:a=11,b=22,c=33a=55,b=22,c=33a=77,b=99,c=33a=8,b=88,c=88848函数的说明与使用函数的参数可缺省参数(参数的默认值)只能为函数最后面的连续若干个参数设置默认值,且在调用处也只能缺省后面的连续若干个实参例如:49voidfunc(inta,intb=2,intc=3);
//OK!voidfunc(inta=1,intb,intc=3);
//ERROR!
voidfunc(inta=1,intb=2,intc);
//ERROR!
对第一个函数说明,采用如下的调用语句:
func(1,22,333);
//OK!调用时给出所有实参
func();
//ERROR!参数a没有默认值
func(10,20);
//OK!参数c默认为3
func(5,,9);//ERROR!调用处也只能缺省后面的连续若干个实参
函数的说明与使用函数调用过程中参数的传递一般传递过程(赋值传递)发生函数调用,转到函数体执行根据函数的形参,分配内存空间将实参赋值给形参,即为形参分配的存储空间赋值,此时实参在函数体内失效,形参有效函数执行完毕,返回到主调函数,形参所占的空间自动回收,形参失效50函数的说明与使用函数调用过程中参数的传递按地址传递过程发生函数调用,转到函数体执行根据代表地址的参数,在以该参数为首地址的空间中进行各种处理数组名指针引用函数执行完毕,无论是否有返回值,函数体对内存空间进行的修改将保留,对主调函数仍然有效51函数的说明与使用函数的参数能够作为函数参数的数据类型基本类型及其派生类型赋值参数传递数组、指针、引用等导出类型、按地址传递参数类类型、结构类型、联合类型等用户定义类型赋值参数传递按地址参数传递52函数的说明与使用函数的参数一维数组作为函数的参数,数组大小可以指定,也可以不指定,但是多维数组除了第一维之外,其它维的大小必须指定函数原型intsearchArray(int[],int);intsearchArray(int[10],int);函数定义intsearchArray(inta[],intb){//写为a[10]也可以
returna[b];}53函数的说明与使用函数的参数多维数组(以二维数组为例)作为函数的参数,参数表的数组参数可以写为inta[][10]inta[10][10]
但不可以写为:inta[][];inta[20][];一维数组以及多维数组的第一维大小,形参、实参可以不对应实参为a[10],形参可以定义为x[6]54函数的说明与使用函数的参数数组作为函数参数,是将实参数组的首地址传递给形参,而不是将数组的所有元素传递给形参在函数体中,根据形参数组首地址和下标指示的偏移量访问数组元素能够表示地址的数据类型数组首地址(第一个元素的地址)指针引用55函数的说明与使用函数的参数【例5.5】一维数组作参数举例:设计函数,实现两个字符数组的连接。56#include<iostream>usingnamespacestd;voidstrcat(chars[],charct[]){
inti=0,j=0;
while(s[i]!=0)i++;
while(ct[j]!=0)s[i++]=ct[j++];s[i]='\0';}函数的说明与使用函数的参数【例5.5】主函数部分57intmain(void){
chara[40]="李明";
charb[20]="是东南大学学生";strcat(a,b);//实参为数组名cout<<a<<endl;//打印字符数组a
return0;}函数的说明与使用函数的参数【例5.6】多维数组做函数参数举例:设计函数,实现矩阵的转置和矩阵乘法,并在主函数中验证用二维数组存储矩阵由于数组作为参数,传递的是数组的首地址,因此,在函数中对作为形参的数组进行的操作,能够直接反映到实参数组中58函数的说明与使用【例5.6】函数原型及主函数59#include<iostream>usingnamespacestd;voidinverse(int[3][6],int[6][3]);//转置矩阵voidmulti(int[6][3],int[3][4],int
[6][4]);//矩阵乘法voidoutput(int[6][4]);//矩阵输出intmain(){
intmiddle[6][3],result[6][4];
intmatrix1[3][6]={8,10,12,23,1,3,5,7,9,2,4,6, 34,45,56,2,4,6};
intmatrix2[3][4]={3,2,1,0,-1,-2,9,8,7,6,5,4};inverse(matrix1,middle);//实参为数组名multi(middle,matrix2,result);output(result);
return0;}函数的说明与使用【例5.6】转置和乘法函数定义60void
inverse(intmatrix1[3][6],intmiddle[6][3]){//转置
int
i,j;
for
(i=0;i<3;i++)
for(j=0;j<6;j++)
middle[j][i]=matrix1[i][j];
return;}void
multi(intmiddle[6][3],intmatrix2[3][4],intresult[6][4]){
int
i,j,k;
//矩阵乘法
for
(i=0;i<6;i++){
for
(j=0;j<4;j++){
result[i][j]=0;
for
(k=0;k<3;k++) result[i][j]+=middle[i][k]*matrix2[k][j];}}
return;}函数的说明与使用【例5.6】矩阵输出函数定义61voidoutput(intresult[6][4]){//矩阵输出
cout<<"result"<<'\n';
inti,j;
for(i=0;i<6;i++){
for(j=0;j<4;j++)cout<<setw(4)<<result[i][j]<<"";cout<<'\n';}
return;}函数的说明与使用函数的参数变量的引用作为函数的形式参数赋值形参:未被说明为引用(&)的参数赋值形参和实参直观上是一致的,实际是不同的在内存中的地址不同引用形参——变量的引用为变量起“别名”在作为形参的变量前加符号“&”与变量的内存地址相同需要在函数中改变实参值并将变化反映到主调函数的时候,用作为实参的变量引用是一种方法还可以用指针做形参操作变量地址62函数的说明与使用函数的参数赋值形参赋值形参的函数调用过程和参数传递机制在执行函数调用时,在检查函数名及参数表之后,为赋值参数分配内存计算各对应的实参表达式,并把计算的值赋给刚刚创建的参数变量开始函数体的运行。凡是赋值形参,在函数的每次调用时,都必须为每一个赋值形参创建一个新的参数变量。63函数的说明与使用函数的参数赋值形参实参表达式:函数调用语句中,与赋值形参相对应的实参可以是指定类型的常量、变量或表达式。在执行函数调用时应把该表达式的值计算出来,作为初值赋给刚刚为赋值形参创建的参数变量。这是赋值调用方式名称的由来。为赋值形参创建的参数变量是局限于函数体运行的局部变量,它作为该形参的一个实例,参加函数体程序块的这次运行,一旦运行完毕,这个参数变量就被撤消64函数的说明与使用函数的参数实参表达式作参数例如,函数原型:intadd(inta,intb);调用函数时,可以采用如下方式:……intmain(){
intx=5; intc=add(1,4*x+2);//实参为表达式 ……}65函数的使用和说明函数的参数【例5.7】编写函数,实现两个整数的交换输入两个整数输出初始值交换输出交换后的值66voidswap(intx,inty){
inttemp=x;x=y;y=temp;}函数的说明和使用【例5.7】主函数部分#inlcude<iostream>usingnamespacestd;voidswap(intx,inty){.....}intmain(){
inta,b; cin>>a>>b; cout<<“a=“<<a<<“b=“<<b<<endl; swap(a,b); cout<<“a=“<<a<<“b=“<<b<<endl;
return0;}67函数的说明与使用【例5.7】分析程序运行结果显示,a和b的值没有进行交换!主函数运行时,为变量a和b分配存储空间调用函数swap时,为变量x和y分配存储空间函数体中交换了x和y存储单元的值a和b所在存储单元的值没有交换输出的仍然是a和b所在存储单元的值68函数的说明与使用函数的参数引用形参函数定义的参数表中,名字前加上符号&的参数为引用形参。例如voidswap(int&a,int&b);引用形参在调用过程中的参数传递机制函数的调用语句中对应于引用形参的实参必须是同一类型的变量,非变量的表达式则不允许。参数传递的内容不是实参的值,而是地址,其实际的效果是令对应的引用形参在调用过程中,作为一个变量名指向作为实参的这个变量,在引用调用过程中并不创建新的参数变量。这一点有别于赋值调用69函数的说明与使用函数的参数引用形参引用形参在调用过程中的参数传递机制在函数体程序块的运行中,引用形参的每次出现,由于它现在已经是指向实参变量,因此相当于全用实参变量所代替。即起到了所谓的“换名”的作用。在函数体程序运行结束,控制转回调用点时,该引用形参与实参变量的对应关系也就终止了。但是在调用过程中对于这个实参变量的所有处理和操作的结果,却保留下来。这一点也是区别于赋值调用的
70函数的说明与使用函数的参数引用形参设计函数在下面两种情形时,建议采用引用参数需要改变某些变量的值(上述函数swap就是一例)对于占内存较多的数据参数,为了不另建新的参数变量以节省内存为了保证实参不在函数中被修改,可在形参说明中加上const说明,例如:
complexadd(constcomplex&
a,constcomplex&
b);而对于赋值形参,则无此必要。71函数的说明与使用函数的参数赋值调用所调用的函数参数为赋值参数引用调用所调用的函数参数为引用参数举例 voidprintStar(intk,intn);
//它所用的两个参数均为赋值参数
voidswap(int&x,int&y);
//它所用的两个参数均为引用参数
intmyFunc(inta,float&b);
//它所用的第一个参数为赋值参数,另一个为引用参数72函数的说明与使用函数的参数【例5.7】中的交换函数,采用引用作为参数 voidswap(int&x,int&y){
inttemp=x;x=y;y=temp; }引用参数&x和&y是实参的别名,函数中对引用形参的操作实际上就是对实参的操作
73函数的说明与使用函数的参数引用调用方式,实际上传递的是参数的地址(实参的地址),函数体中对形参的操作实际上是对实参地址的操作希望在函数中修改实参值并反映到主调函数中的时候,采用引用调用方式指针、数组等能够表示地址的数据类型作为形参,,可以起到与引用形参相同的作用【例5.5】和【例5.6】是数组作为参数,在函数体中修改数组并反映到主调函数的例子74函数的说明与使用函数的参数从赋值或引用参数的角度看函数的调用过程判断形参是赋值参数还是引用参数将对应实参表达式的值赋给赋值形参(若参数为赋值参数的话);用实参变量替换相应的形参(若参数为引用参数的话);按各形参的“当前值”(或已被“赋值”,或已被“换名”)去执行一遍函数体并返回调用处75函数的说明与使用函数的参数赋值调用和引用调用传值方式的区别通过赋值参数来传值的方式是一种“单向传值”方式,它只可向被调函数的形参“传入”值,而不可通过该形参“传出”值
通过引用参数来传值的方式是一种“双向传值”方式,它不仅可向被调函数的形参“传入”值,而且还可通过该形参“传出”值76函数的说明与使用函数的参数【例5.8】赋值调用和引用调用举例#include<iostream>usingnamespacestd;voidswap(int&a,int&b,intc,intd);//函数原型//前两个为引用参数,可“双向传值”//后两个为赋值参数77函数的说明与使用intmain(){ inti=1,j=2,k=77,n=88;
cout<<“---Inmain,beforcallingf1---"<<endl; cout<<"i,j,k,n="<<i<<""<<j<<""<<k<<""<<n<<endl<<endl; swap(i,j,k,n);
//注意,调用后实参变量i、j的值进行了改变 //而k与n的值并不改变
cout<<"---Inmain,aftercallingf1---"<<endl; cout<<"i,j,k,n="<<i<<""<<j<<""<<k<<""<<n<<endl;}78函数的说明与使用voidswap(int&a,int&b,intc,intd)
{
/*前两个为引用参数,后两个为赋值参数。对引用参数而言,调用时,将用对应实参变量来替换它们。即是说,被调函数中对形参值的使用与改变,就是对主调函数中调用语句处所对应实参变量值的直接使用与改变(“双向传值”)*/
cout<<"---Enterf1---"<<endl; cout<<"a,b,c,d="<<a<<""<<b<<""<<c<<""<<d<<endl<<endl; inttmp;
tmp=a;a=b;b=tmp; //交换a与b的值
tmp=c;c=d;d=tmp; //交换c与d的值
cout<<"---Intheendoff1---"<<endl; cout<<"a,b,c,d="<<a<<""<<b<<""<<c<<""<<d<<endl<<endl;}79函数的说明与使用程序运行结果---Inmain,beforcallingf1---i,j,k,n=127788
---Enterf1---a,b,c,d=127788
---Intheendoff1---a,b,c,d=218877
---Inmain,aftercallingf1---i,j,k,n=21778880函数的说明与使用函数的重载(后面具体讲解)多个函数使用相同的函数名函数重载必须满足下列条件之一参数表中对应的参数类型不同参数表中参数个数不同参数表中不同类型参数的次序不同例如,三个同名函数可以声明为:printStar();printStar(int);printStar(int,int);81函数的说明与使用内联函数可在一般的函数说明前冠以关键字inline,称这样的函数为内联函数。说明方式:
inline<函数类型><函数名>(<形参表>)
{ <函数体> }82函数的说明与使用内联函数在编译过程中,凡内联函数,系统均把它的执行代码插入到该函数的每个调用点处(以取代那一函数调用),从而使程序执行过程中,每次对该函数调用时不需控制转移,可节省执行时间由于每个调用点处均出现那一函数的执行代码拷贝,相对来说使用内联函数后会扩大其代码空间83函数的说明与使用内联函数举例#include<iostream.h>inlineintmax(intx,inty){//内联函数max
return(x>y?x:y);}voidmain(){
inta,b; cout<<"Inputa,b:"; cin>>a>>b; cout<<"max(a,b)="<<max(a,b)<<endl;
//对内联函数max的调用}84函数的说明与使用内联函数说明内联函数的函数体一般讲不宜过大,以1--5行为宜。凡在类体中定义的成员函数(见第7章)均隐含为内联函数85函数的说明与使用函数使用的其它问题(后面具体讲解)函数的生存期与作用域函数中变量的生存期与作用域参数函数体内的局部变量函数体内的全局变量函数的存储属性外部存储属性静态存储属性函数间的数据传递方式86函数的说明与使用函数使用举例【例5.9】三色冰淇淋程序由冰激凌商提出的问题:有28种颜色的原料,可以组合成多少种3色冰激凌问题归结为计算排列数与组合数。本示例计算排列数A(elements,selections)及组合数C(elements,selections)。如:
A(3,2)=6,C(3,2)=3; A(28,3)=19656,C(28,3)=3276。87函数的说明与使用【例5.9】分析求排列数的公式A(ele,sel):A(ele,sel)=ele!/(ele-sel)!求组合数的公式C(ele,sel):C(ele,sel)=A(ele,sel)/sel!设计阶乘函数,求上述各阶乘的值并可以实现排列数和组合数的求值88函数的说明与使用【例5.9】计算阶乘的函数定义longfactorial(intnumber){
longvalue=1;
while(number>1){ value*=number; number--; }
returnvalue;}89函数的说明与使用【例5.9】主程序部分#include<iostream>usingnamespacestd;longfactorial(intnumber);//函数原型intmain(){ inti,selections,elements;
//计算A(elements,selections) //以及C(elements,selections)
cout<<"Numberofselections:"; cin>>selections;//输入整数selections cout<<"Outofhowmanyelements:"; cin>>elements;//输入整数elements doubleanswer=elements; intele=elements;90函数的说明与使用
answer=factorial(elements)/factorial(elements-selects); cout<<“A(”<<elements<<“,”<<selections<<“)=”<<answer<<endl;//输出排列数A(ele,sel)之结果
//组合数C的求法:
answer/=factorial(selections); cout<<"C("<<elements<<","<<selections<<")="<<answer<<endl;//输出组合数C(ele,sel)之结果
return0;}
91第5章函数与重载函数的说明与使用1函数的嵌套与递归2函数与运算符重载3函数与C++程序结构492函数的嵌套与递归函数的嵌套一个函数的函数体中包含一个或多个函数调用语句,即称为函数嵌套嵌套的含义是,如果函数A要调用函数B,也就是说,函数A的定义要依赖于函数B的定义。因此函数B的定义或函数B的原型必须出现在函数A的定义语句之前。另一方面,函数A调用函数B,在调用A的过程中,即执行A的函数体过程中,调用B,也就是中途把程序控制转到B的函数体,在执行结束后再返回到A的函数体中93函数的嵌套与递归函数的嵌套示意图94函数的嵌套与递归函数的嵌套函数嵌套调用所占用的内存空间(如赋值参数的创建等等)用堆栈(stack)的方式管理。一般这种堆栈所分配的空间是有限的,因此函数互相嵌套的层数也是有限的,依编译系统不同,其允许的嵌套层数也可能不同95函数的嵌套与递归函数的嵌套函数嵌套调用过程中的栈结构96函数的嵌套与递归函数的嵌套函数嵌套调用过程中的栈结构主函数运行时,栈区的情况97函数的嵌套与递归函数的嵌套函数嵌套调用过程中的栈结构98函数的嵌套与递归函数的嵌套函数嵌套调用过程中的栈结构发生函数调用时(调用a函数),“保护”主函数当前的运行状态,记录被调函数的返回地址99函数的嵌套与递归函数的嵌套函数嵌套调用过程中的栈结构100函数的嵌套与递归函数的嵌套函数嵌套调用过程中的栈结构运行a函数,记录a函数的参数和局部变量的值101函数的嵌套与递归函数的嵌套函数嵌套调用过程中的栈结构102函数的嵌套与递归函数的嵌套函数嵌套调用过程中的栈结构调用b函数,保护a函数的当前状态,记录b函数的返回地址103函数的嵌套与递归函数的嵌套函数嵌套调用过程中的栈结构104函数的嵌套与递归函数的嵌套函数嵌套调用过程中的栈结构运行b函数,记录b函数的参数和局部变量的值105函数的嵌套与递归函数的嵌套函数嵌套调用过程中的栈结构106函数的嵌套与递归函数的嵌套函数嵌套调用过程中的栈结构b函数运行结束并返回,b函数的变量和参数失效,变量和参数由栈区“弹出”,根据b函数的返回地址返回,读取a函数的运行状态继续运行a函数107函数的嵌套与递归函数的嵌套函数嵌套调用过程中的栈结构108函数的嵌套与递归函数的嵌套函数嵌套调用过程中的栈结构a函数运行结束并返回,a函数的变量和参数失效,变量和参数由栈区“弹出”,根据a函数的返回地址返回,读取主函数的运行状态继续运行主函数109函数的嵌套与递归函数的嵌套【例5.10】编写程序,用冒泡排序的算法对数组中的元素按照由小到大的顺序进行排序输入数组调用排序函数进行排序参数:待排序的数组返回:空由于参数为数组,即数组的首地址,函数体中处理数组的地址,因此,对数组的修改可以直接反映到主调函数中输出排序后的数组110函数的嵌套与递归【例5.10】函数bubSort代码voidbubSort(inta[],intn){
for(inti=0;i<n;i++){
for(intj=n-1;j>i;j--){
if(a[j]<a[j-1]){ swap(a[j],a[j-1]); } } }}111函数的嵌套与递归【例5.10】函数swap代码voidswap(int&x,int&y){ inttemp=x; x=y; y=temp;}112函数的嵌套与递归【例5.10】主程序代码#include<iostream>#include<stdlib>#include<time>#include<iomanip>usingnamespacestd;constintn=100;intmain(){
intb[n]; srand((unsigned)time(NULL));
for(inti=0;i<n;i++) b[i]=rand();113函数的嵌套与递归 bubSort(b,n);
for(i=0;i<n;i++){ cout<<setw(5)<<b[i];
if(i%10==0) cout<<endl; }
return0;}/*设计函数,分别按产生顺序和排序后的顺序输出随机数,输出宽度为5,每输出10个数换行*/114函数的嵌套与递归函数的递归C++允许函数自己调用自己(如A函数可以调用A函数本身,称为直接递归)。也允许A函数调用B函数,而后B函数又调用A函数(从而形成间接递归)。但不论使用哪种递归,程序员都应保障递归函数在执行若干次后能够“退出”递归(不再进行递归调用,也即能够实现递归出口)115函数的嵌套与递归函数的递归递归函数的执行分为“递推”和“回归”两个过程,这两个过程由递归终止条件控制,即逐层递推,直至递归终止条件,然后逐层回归。每次调用发生时都首先判断递归终止条件。116函数的嵌套与递归函数的递归递归调用同普通的函数调用一样,每当调用发生时,在栈中分配单元保存返回地址以及参数和局部变量;而与普通的函数调用不同的是,由于递推的过程是一个逐层调用的过程,因此存在一个逐层连续的参数入栈过程,直至遇到递归终止条件时,才开始回归,这时才逐层释放栈空间,返回到上一层,直至最后返回到主调函数117函数的嵌套与递归函数的递归【例5.11】计算年龄【例5.12】求阶乘,三色冰淇淋程序【例5.13】反序输出问题【例5.14】输入一个整数,将数字反序输出【例5.15】汉诺塔问题【例5.16】快速排序118函数的嵌套与递归函数的递归【例5.11】有5个人坐在一起,问第5个人多少岁?他说,比第4个人大两岁。问第4个人多少岁?他说,比第3个人大两岁。问第3个人多少岁?他说,比第2个人大两岁。问第2个人多少岁?他说,比第1个人大两岁。问第1个人多少岁?他说是十岁。请问,第5个人多大?欲求第5个人的年龄,就必须先知道第4个人的年龄,欲求第4个人的年龄,就必须先知道第3个人的年龄,欲求第3个人的年龄,就必须先知道第2个人的年龄,欲求第2个人的年龄,就必须先知道第1个人的年龄,而且每个人的年龄都比前一个人大两岁119函数的嵌套与递归【例5.11】分析5个人的年龄可以分别表示为age(5)=age(4)+2age(4)=age(3)+2age(3)=age(2)+2age(2)=age(1)+2age(1)=10通项式为:age(1)=10age(n)=age(n-1)+2120函数的嵌套与递归【例5.11】递归过程图示121函数的嵌套与递归【例5.11】求年龄的递归函数age()定义intage(intn){
intperson_age;
if(n==1) person_age=10;
else person_age=age(n-1)+2;
returnperson_age;}122函数的嵌套与递归【例5.11】主程序部分#include<iostream>usingnamespacestd;intmain(){ cout<<“第5个人的年龄为:”<<age(5)<<“岁”; cout<<endl;
return0;}123函数的嵌套与递归函数的递归【例5.12】用递归函数求整数n的阶乘1!=1n!=n*(n-1)!递归函数定义longfac(intn){
if((n==1)||(n==0))
return1;
else
returnn*fac(n-1);}124函数的嵌套与递归函数的递归【例5.13】反序输出:从键盘输入10个int型数,而后按输入的相反顺序输出它们。例如:输入:12345678910
输出:10987654321125函数的嵌套与递归【例5.13】分析递推过程将输入的10个数,由第10个推到第1个回归过程由第1个数开始输出后项依赖于前项的“输出”,而不是像以前程序那样依赖前一项的值126函数的嵌套与递归【例5.13】递归过程图示127函数的嵌套与递归【例5.13】递归函数定义voidinv(intn){
inti; cin>>i;//输入整数
if(n==1) cout<<“Theresult:”<<endl;
else inv(n-1); cout<<i<<““;//输出整数,每次递归调用返 //回之后都要执行}128函数的嵌套与递归【例5.13】主程序部分#include<iostream>usingnamespacestd;intmain(){ cout<<“Input10integers:”<<endl; inv(10); cout<<endl;
return0;}129函数的嵌套与递归函数的递归【例5.14】反序输出一个正整数的各位数值,如输入231,应输出132递归函数定义如下: voidconv(intn){
if(n<10){ cout<<n;
return; }//递归出口 cout<<n%10; conve(n/10);//递归 }130函数的嵌套与递归【例5.14】递归过程图示131函数的嵌套与递归【例5.14】用非递归函数实现intconv(intn){
if(n<0) cout<<“Pleaseinputapositivenumber!”;
else{
do{ cout<<n%10;
n=/10;
}while(n!=0); }}132函数的嵌套与递归【例5.14】主程序部分#include<iostream>usingnamespacestd;voidmain(void){
intt;
cout<<”Inputapositivenumber:”;
cin>>t;
cout<<endl;
conv(t);
return0;}133函数的嵌套与递归函数的递归【例5.15】古印度的著名智力测验问题:有三个立柱A、B、C,在A柱上穿有大小不等的圆盘64个,较大的圆盘在下,较小者在上。要求借助于B柱将A柱上的64个圆盘移到C柱,规则为:(1)每次只能把一个柱上最上面的圆盘移至另一个柱的最上面;(2)每个柱上总保持较大的圆盘在下,较小者在上。编制程序,实现将任意n个圆盘从A柱借助于B柱移到C柱,并显示出全部移动过程134函数的嵌套与递归【例5.15】分析总任务(圆盘数为n的任务):把A柱上的n个圆盘,借助于B柱,按规则移到C柱上(移动规则:一次移一片,大片不可压小片)。靠调用自定义函数hanoi来完成:hanoi(n,'A','B','C');135A柱B柱C柱函数的嵌套与递归【例5.15】分析A柱只有一个盘子的情况:A柱C柱;A柱有两个盘子的情况:小盘A柱B柱,大盘A柱C柱,小盘B柱C柱。A柱有n个盘子的情况:将此问题看成上面n-1个盘子和最下面第n个盘子的情况。n-1个盘子A柱B柱,第n个盘子A柱C柱,n-1个盘子B柱C柱。问题转化成搬动n-1个盘子的问题,同样,将n-1个盘子看成上面n-2个盘子和下面第n-1个盘子的情况,进一步转化为搬动n-2个盘子的问题,……,类推下去,一直到最后成为搬动一个盘子的问题。136函数的嵌套与递归【例5.15】分析1.n-1个盘子A柱B柱,借助于C柱;2.第n个盘子A柱C柱;3.n-1个盘子B柱C柱,借助于A柱;其中步骤1和步骤3继续递归下去,直至搬动一个盘子为止。由此,可以定义两个函数,一个是递归函数,命名为hanoi(intn,charsource,chartemp,chartarget),实现将n个盘子从源柱source借助中间柱temp搬到目标柱target;另一个命名为move(charsource,chartarget),用来输出搬动一个盘子的提示信息。137函数的嵌套与递归【例5.15】函数move的定义voidmove(char
source,chartarget){cout<<source<<“=>"<<target<<endl;}138函数的嵌套与递归【例5.15】递归函数hanoi的定义voidhanoi(intn,charsource,chartemp,chartarget){
if(n==1) move(source,target);
else{
//将n-1个盘子搬到中间柱 hanoi(n-1,source,target,temp);
//将最后一个盘子搬到目标柱
move(source,target);
//将n-1个盘子搬到目标柱
hanoi(n-1,temp,source,target); } }
139函数的嵌套与递归【例5.15】主程序部分#include<iostream>usingnamespacestd;voidmove(char,char);voidhanoi(int,char,char,char);intmain(){ intn; cout<<"输入盘子数:"<<endl; cin>>n; hanoi(n,'A','B','C'); return0;}140函数的嵌套与递归141hanoi(2,’A’,’C’,’B’)A→Chanoi(3,’A’,’B’,’C’)
hanoi(1,’A’,’B’,’C’)hanoi(1,’B’,’C’,’A’)C→BB→AA→CB→Chanoi(1,’A’,’B’,’C’)A→Bhanoi(1,’C’,’A’,’B’)A→Chanoi(2,’B’,’A’,’C’)汉诺塔程序执行框图输入盘子数:3函数的嵌套与递归【例5.15】移动次序1142ABC函数的嵌套与递归【例5.15】移动次序2143ABC函数的嵌套与递归【例5.15】移动次序3144ABC函数的嵌套与递归【例5.15】移动次序4145ABC函数的嵌套与递归【例5.15】移动次序5146ABC函数的嵌套与递归【例5.15】移动次序6147ABC函数的嵌套与递归【例5.15】移动次序7148ABC函数的嵌套与递归【例5.15】移动次序8149ABC函数的嵌套与递归函数的递归【例5.16】递归程序实现排序。#include<iostream>#include<cstdlib>#include<ctime>#include<iomanip>usingnamespacestd;const
intn=10;voidswap(int&,int&);intfindpivot(int[],int,int);intpartition(int[],int,int,int);voidquickSort(int[],int,int);150函数的嵌套与递归【例5.16】主程序部分intmain(){ intb[n]; srand((unsigned)time(NULL)); cout<<"随机数:"<<endl;
for(inti=0;i<n;i++){ b[i]=rand()%100; cout<<setw(5)<<b[i];
if((i+1)%10==0) cout<<endl; } quickSort(b,0,n-1);151函数的嵌套与递归【例5.16】主程序部分
cout<<endl<<"排序后:"<<endl;
for(i=0;i<n;i++){ cout<<setw(5)<<b[i];
if((i+1)%10==0) cout<<endl; }
return0;}152函数的嵌套与递归【例5.16】函数swap定义voidswap(int&x,int&y){
inttemp=x;x=y;y=temp;}153函数的嵌套与递归【例5.16】函数findpivot定义intfindpivot(inta[],inti,intj){
return(i+j)/2;}154函数的嵌套与递归【例5.16】函数partition定义intpartition(inta[],intl,intr,intpivot){
do{
while(a[++l]<pivot);
while(r&&a[--r]>pivot); swap(a[l],a[r]); }while(l<r); swap(a[l],a[r]);
returnl;}155函数的嵌套与递归【例5.16】函数quickSort定义voidquickSort(inta[],inti,intj){
intpivotindex=findpivot(a,i,j); swap(a[pivotindex],a[j]);
intk=partition(a,i-1,j,a[j]); swap(a[k],a[j]);
if((k-i)>1) quickSort(a,i,k-1);
if((j-k)>1) quickSort(a,k+1,j);}156第5章函数与重载函数的说明与使用1函数的嵌套与递归2函数与运算符重载3函数与C++程序结构4157函数与运算符重载函数重载函数重载实际上是函数名重载,即支持多个不同的函数采用同一名字例如:intabs(intn){return(n<0?-n:n);}floatabs(floatf){if(f<0)f=-f;
returnf;}doubleabs(doubled){if(d<0)return-d;returnd;}三个函数都是求绝对值,采用同一个函数名,更符合人们的习惯158函数与运算符重载函数重载例如在程序中经常出现这样的情况:对
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年博物馆教育资源与多元智能对接
- 2026年餐饮业预防食物中毒关键控制点培训
- 2026年止痛药合理使用讲座课件
- 2026年小学生美术创造力培养的实践与效果评估
- 2026年文化金融支持绿色文化项目发展
- 2026年银行客户经理信贷业务入门与风控要点
- 2026年原辅料入厂检验标准制定与检验频次优化
- 肌内注射:中职护理案例分析
- 2026年平台经济下外卖骑手权益保障与职业发展困境调研
- 2026年银行突发公共卫生事件演练
- 教育局中小学考试命题管理方案
- 光大金瓯资产管理有限公司笔试
- 2025年中国邮政集团有限公司湖北省分公司招聘笔试备考试题及完整答案详解1套
- 2025年建筑施工特种作业人员考试建筑电焊工题库(附答案)
- 构建人类命运共同体+课件-2025-2026学年高中政治统编版选择性必修一
- 2025年善意的谎言辩论会材料及流程
- 2025年辽宁卷历史高考试卷(原卷+答案)
- 检验科个人防护培训课件
- 小儿骨科课件
- 2025年不动产登记业务知识试题及答案
- 2025年内部审计人员考试题库
评论
0/150
提交评论