【文档说明】C++语言程序设计三.ppt,共(74)页,712.500 KB,由小橙橙上传
转载请保留链接:https://www.ichengzhen.cn/view-2184.html
以下为本文档部分文字说明:
2021/7/261(最新整理)C++语言程序设计(清华大学郑莉)三2021/7/262第三章函数清华大学郑莉C++语言程序设计C++语言程序设计清华大学郑莉2021/7/263本章主要内容⚫函数的定义和调用⚫函数间的参数传递⚫内联函数⚫
带默认形参值的函数⚫函数重载⚫C++系统函数⚫深度探索C++语言程序设计清华大学郑莉2021/7/264函数的定义⚫函数是面向对象程序设计中,对功能的抽象⚫函数定义的语法形式类型标识符函数名(形式参数表){语句序列}函数的声明与使用是被初始化的内部变量,寿命和可见性仅限于函数内部若无返
回值,写voidC++语言程序设计清华大学郑莉2021/7/265函数的定义⚫形式参数表<type1>name1,<type2>name2,...,<typen>namen⚫函数的返回值–由return语句给出,例如:r
eturn0–无返回值的函数(void类型),不必写return语句。函数的声明与使用C++语言程序设计清华大学郑莉2021/7/266函数的调用⚫调用前先声明函数:–若函数定义在调用点之前,则无需另外声明;–若函数定义在调用点之后,则需要在调用函数前按如下形式声明函数原
型:类型标识符被调用函数名(含类型说明的形参表);⚫调用形式函数名(实参列表)⚫嵌套调用–函数可以嵌套调用,但不允许嵌套定义。⚫递归调用–函数直接或间接调用自身。函数的声明与使用C++语言程序设计清华大
学郑莉2021/7/267例3-1编写一个求x的n次方的函数#include<iostream>usingnamespacestd;//计算x的n次方doublepower(doublex,intn){doubleval=1.0;whil
e(n--)val*=x;returnval;}intmain(){cout<<"5tothepower2is"<<power(5,2)<<endl;return0;}函数的声明与使用C++语言程序设计清华大学郑莉2021/
7/268运行结果:5tothepower2is25例3-1编写一个求x的n次方的函数函数的声明与使用C++语言程序设计清华大学郑莉2021/7/269例3-2数制转换题目:输入一个8位二进制数,将其转换为十进制数输出。例如:11012=1(23)+1(22)+0(21)+1(20)=1310所以
,如果输入1101,则应输出13函数的声明与使用#include<iostream>usingnamespacestd;//计算x的n次方doublepower(doublex,intn);intmain(){intvalue=0;cout<<"Enteran8
bitbinarynumber";for(inti=7;i>=0;i--){charch;cin>>ch;if(ch=='1')value+=static_cast<int>(power(2,i));}cout<<"Decimalvalueis"<<value<<endl;
return0;}doublepower(doublex,intn){doubleval=1.0;while(n--)val*=x;returnval;}运行结果:Enteran8bitbinaryn
umber01101001Decimalvalueis10510C++语言程序设计清华大学郑莉2021/7/2611例3-3编写程序求π的值其中arctan用如下形式的级数计算:直到级数某项绝对值不大于10-15为止;π和x均为doub
le型。函数的声明与使用−=2391arctan451arctan16π357arctan357xxxxx=−+−+#include<iostream>usingnamespacestd;doublearct
an(doublex){doublesqr=x*x;doublee=x;doubler=0;inti=1;while(e/i>1e-15){doublef=e/i;r=(i%4==1)?r+f:r-f;e=e*sqr;i+=2;}returnr;
}12intmain(){doublea=16.0*arctan(1/5.0);doubleb=4.0*arctan(1/239.0);//注意:因为整数相除结果取整,如果参数写1/5,1/239,结果就都是0cout<<"P
I="<<a-b<<endl;return0;}运行结果:PI=3.1415913C++语言程序设计清华大学郑莉2021/7/2614例3-4⚫寻找并输出11~999之间的数m,它满足m、m2和m3均
为回文数。–回文:各位数字左右对称的整数。例如:11满足上述条件112=121,113=1331。⚫分析:–10取余的方法,从最低位开始,依次取出该数的各位数字。按反序重新构成新的数,比较与原数是否相等,若相
等,则原数为回文。函数的声明与使用#include<iostream>usingnamespacestd;//判断n是否为回文数boolsymm(unsignedn){unsignedi=n;unsignedm=0;while(i>0){m=m*10+i%
10;i/=10;}returnm==n;}15intmain(){for(unsignedm=11;m<1000;m++)if(symm(m)&&symm(m*m)&&symm(m*m*m)){cout<<"m="<<m;cout<<"m*m="
<<m*m;cout<<"m*m*m="<<m*m*m<<endl;}return0;}16运行结果:m=11m*m=121m*m*m=1331m=101m*m=10201m*m*m=1030301m=111m*m=12321m*m*m=136763117C++语言程序设计
清华大学郑莉2021/7/2618例3-5计算如下公式,并输出结果:其中r、s的值由键盘输入。sinx的近似值按如下公式计算,计算精度为10-6:函数的声明与使用=−−−−=+−+−=1121753)!12()1(!7!5!3!1sinnnnnxxxxxx2
22222sinsinr1sin()r2rsskrss+=当当#include<iostream>#include<cmath>//对C++标准库中数学函数的说明usingnamespa
cestd;constdoubleTINY_VALUE=1e-10;doubletsin(doublex){doubleg=0;doublet=x;intn=1;do{g+=t;n++;t=-t*x*x/(2*n-1)/(2*n-2);}
while(fabs(t)>=TINY_VALUE);returng;}19intmain(){doublek,r,s;cout<<"r=";cin>>r;cout<<"s=";cin>>s;if(r*r<=s*s)k=sqrt(tsin(r)*t
sin(r)+tsin(s)*tsin(s));elsek=tsin(r*s)/2;cout<<k<<endl;return0;}运行结果:r=5s=81.3778120C++语言程序设计清华大学郑莉2021/7/2621例3-6投骰子的随机游戏每个骰子有六面,点数分别为1、
2、3、4、5、6。游戏者在程序开始时输入一个无符号整数,作为产生随机数的种子。每轮投两次骰子,第一轮如果和数为7或11则为胜,游戏结束;和数为2、3或12则为负,游戏结束;和数为其它值则将此值作为自己的点数,继续第
二轮、第三轮...直到某轮的和数等于点数则取胜,若在此前出现和数为7则为负。由rolldice函数负责模拟投骰子、计算和数并输出和数。函数的声明与使用⚫rand函数原型:intrand(void);所需头文件:<cstdlib>功能和返回值:求出并返回一个伪随机数⚫srand函数原型:vo
idsrand(unsignedintseed);参数:seed产生随机数的种子。所需头文件:<cstdlib>功能:为使rand()产生一序列伪随机整数而设置起始点。使用1作为seed参数,可以重新初化rand()。22#include<iostre
am>#include<cstdlib>usingnamespacestd;//投骰子、计算和数、输出和数introllDice(){intdie1=1+rand()%6;intdie2=1+rand()%6;intsum=die
1+die2;cout<<"playerrolled"<<die1<<"+"<<die2<<"="<<sum<<endl;returnsum;}23enumGameStatus{WIN,LOSE,PLAYING};intmain(){intsum
,myPoint;GameStatusstatus;unsignedseed;cout<<"Pleaseenteranunsignedinteger:";cin>>seed;//输入随机数种子srand(seed);/
/将种子传递给rand()sum=rollDice();//第一轮投骰子、计算和数24switch(sum){case7://如果和数为7或11则为胜,状态为WINcase11:status=WIN;break;case2://和数为2、3或12则为负,状态为LOSEcase3
:case12:status=LOSE;break;default://其它情况,游戏尚无结果,状态为PLAYING,记下点数,为下一轮做准备status=PLAYING;myPoint=sum;cout<<"pointis"<
<myPoint<<endl;break;}25while(status==PLAYING){//只要状态仍为PLAYING,就继续进行下一轮sum=rollDice();if(sum==myPoint)//某轮的和数等于点数则取
胜status=WIN;elseif(sum==7)//出现和数为7则为负status=LOSE;}//当状态不为PLAYING时上面的循环结束,以下程序段输出游戏结果if(status==WIN)cout<<"playerwins"<<endl;elsec
out<<"playerloses"<<endl;return0;}26运行结果2:Pleaseenteranunsignedinteger:23playerrolled6+3=9pointis9player
rolled5+4=9playerwins27C++语言程序设计清华大学郑莉2021/7/2628嵌套调用函数的声明与使用main{}调fun1()结束fun1()调fun2()返回fun2()返回①②③⑦④⑤⑥
⑧⑨C++语言程序设计清华大学郑莉2021/7/2629例3-6输入两个整数,求平方和。#include<iostream>usingnamespacestd;intfun2(intm){returnm*m;}intfun1(intx,inty){returnfun2(x)+fun2(y);}函数
的声明与使用intmain(){inta,b;cout<<"Pleaseentertwointegers(aandb):";cin>>a>>b;cout<<"Thesumofsquareofaandb:"<<fun
1(a,b)<<endl;return0;}运行结果:Pleaseentertwointegers(aandb):34Thesumofsquareofaandb:2530C++语言程序设计清华大学郑莉2021/7/2631递归调用⚫函数直接或
间接地调用自身,称为递归调用。⚫递归过程的两个阶段:–递推:4!=4×3!→3!=3×2!→2!=2×1!→1!=1×0!→0!=1未知已知–回归:4!=4×3!=24←3!=3×2!=6←2!=2×1!=2←1!=1×0!=1←0!=1未知
已知函数的声明与使用C++语言程序设计清华大学郑莉2021/7/2632例3-8求n!分析:计算n!的公式如下:这是一个递归形式的公式,应该用递归函数实现。函数的声明与使用−==)0()!1()0(1!nnnnn源程序:#include<ios
tream>usingnamespacestd;unsignedfac(intn){unsignedf;if(n==0)f=1;elsef=fac(n-1)*n;returnf;}33intmain(){unsignedn;cout
<<"Enterapositiveinteger:";cin>>n;unsignedy=fac(n);cout<<n<<"!="<<y<<endl;return0;}运行结果:Enterapositiveinteger:88!=403
2034C++语言程序设计清华大学郑莉2021/7/2635例3-9⚫用递归法计算从n个人中选择k个人组成一个委员会的不同组合数。⚫分析:由n个人里选k个人的组合数=由n-1个人里选k个人的组合数+由n-1个人里选k-1个人的组合数当n=k或k=0时,组合数为1函数
的声明与使用#include<iostream>usingnamespacestd;intcomm(intn,intk){if(k>n)return0;elseif(n==k||k==0)return1;elsereturncomm(n-1,k)+comm(
n-1,k-1);}intmain(){intn,k;cout<<"Pleaseentertwointegersnandk:";cin>>n>>k;cout<<"C(n,k)="<<comm(n,k)<<endl;return0;}运行结果:18
5856836C++语言程序设计清华大学郑莉2021/7/2637例3-10汉诺塔问题有三根针A、B、C。A针上有N个盘子,大的在下,小的在上,要求把这N个盘子从A针移到C针,在移动过程中可以借助B针,每次只允许移动一个盘,且在移
动过程中在三根针上都保持大盘在下,小盘在上。函数的声明与使用ABC分析:将n个盘子从A针移到C针可以分解为下面三个步骤:①将A上n-1个盘子移到B针上(借助C针);②把A针上剩下的一个盘子移到C针上;③将n-1个盘子从B针移到C针上(借助A针);事
实上,上面三个步骤包含两种操作:①将多个盘子从一个针移到另一个针上,这是一个递归的过程。hanoi函数实现。②将1个盘子从一个针上移到另一针上。用move函数实现。38#include<iostream>usingnamespacestd;//把src针的最上面一个盘子移动到dest针上voi
dmove(charsrc,chardest){cout<<src<<"-->"<<dest<<endl;}//把n个盘子从src针移动到dest针,以medium针作为中介voidhanoi(intn,c
harsrc,charmedium,chardest){if(n==1)move(src,dest);else{hanoi(n-1,src,dest,medium);move(src,dest);hanoi(n-1,medium,src,dest);
}}39intmain(){intm;cout<<"Enterthenumberofdiskes:";cin>>m;cout<<"thestepstomoving"<<m<<"diskes:"<<endl;hanoi(m,'A','B','C');return0;}40运行结果:Enterth
enumberofdiskes:3thestepstomoving3diskes:A-->CA-->BC-->BA-->CB-->AB-->CA-->C41C++语言程序设计清华大学郑莉2021/7/2642函数的参数传递机制——传递参数
值⚫在函数被调用时才分配形参的存储单元。⚫实参可以是常量、变量或表达式。⚫实参类型必须与形参相符。⚫传递时是传递参数值,即单向传递。函数的声明与使用C++语言程序设计清华大学郑莉2021/7/2643函
数的参数传递机制——参数值传递举例XN被调函数:主调函数:32.5AD=power(A,3)2.53doublepower(doubleX,intN)函数的声明与使用C++语言程序设计清华大学郑莉2021/7/2644例3-11输入两个整数交换后输出#include<iostre
am>usingnamespacestd;voidswap(inta,intb){intt=a;a=b;b=t;}函数的声明与使用intmain(){intx=5,y=10;cout<<"x="<<x<<"y="<<y<<endl;swap(x,y);cout<<"x="<<x<<"
y="<<y<<endl;return0;}运行结果:x=5y=10x=5y=1045a=b;5x10y5a10b执行主函数中的函数调用swap(x,y);t=a;5x10y5a10b5tb=t;5x10y1
0a5b5t5x10y10a10b5t在swap子函数中返回主函数以后5x10y4646C++语言程序设计清华大学郑莉2021/7/2647函数的参数传递——用引用做形参⚫引用(&)是标识符的别名,例如
:inti,j;int&ri=i;//建立一个int型的引用ri,并将其//初始化为变量i的一个别名j=10;ri=j;//相当于i=j;⚫声明一个引用时,必须同时对它进行初始化,使它指向一个已存在的对象。⚫一旦一个引用被初始化后,就不能改为指向其它对象。⚫引用可以作为形参
voidswap(int&a,int&b){...}函数的声明与使用C++语言程序设计清华大学郑莉2021/7/2648例3-12输入两个整数交换后输出#include<iostream>usingnamespacestd;voidswap(int&a,int
&b){intt=a;a=b;b=t;}intmain(){intx=5,y=10;cout<<"x="<<x<<"y="<<y<<endl;swap(x,y);cout<<"x="<<x<<"y="<<y<<endl;retu
rn0;}函数的声明与使用运行结果:x=5y=10x=10y=5t=a;x5t5x的引用axy510y的引用x的引用aby的引用x的引用abx10y10a=bb=t;y5t5y的引用bxy105swap(x,y);
49C++语言程序设计清华大学郑莉2021/7/2650内联函数声明与使用⚫声明时使用关键字inline。⚫编译时在调用处用函数体进行替换,节省了参数传递、控制转移等开销。⚫注意:–内联函数体内不能有循环语句和switch语句。–内联函数的声明必须出现在内联函数第一次被调用之前
。–对内联函数不能进行异常接口声明。内联函数C++语言程序设计清华大学郑莉2021/7/2651例3-14内联函数应用举例#include<iostream>usingnamespacestd;constdoublePI=3.
14159265358979;inlinedoublecalArea(doubleradius){returnPI*radius*radius;}intmain(){doubler=3.0;doublearea=calArea(r);cout<<area
<<endl;return0;}内联函数C++语言程序设计清华大学郑莉2021/7/2652缺省形参值的作用⚫函数在声明时可以预先给出缺省的形参值,调用时如给出实参,则采用实参值,否则采用预先给出的缺省形参值。⚫例
如:intadd(intx=5,inty=6){returnx+y;}intmain(){add(10,20);//10+20add(10);//10+6add();//5+6}带缺省形参值的函数C+
+语言程序设计清华大学郑莉2021/7/2653缺省形参值的说明次序⚫有缺省参数的形参必须在形参列表的最后,也就是说缺省形参值的右面不能有无缺省值的参数。因为调用时实参与形参的结合是从左向右的顺序。⚫例:intadd(i
ntx,inty=5,intz=6);//正确intadd(intx=1,inty=5,intz);//错误intadd(intx=1,inty,intz=6);//错误带缺省形参值的函数C++语言程序设计清华大学郑莉2021/7/26
54缺省形参值与函数的调用位置⚫如果一个函数有原型声明,且原型声明在定义之前,则缺省形参值必须在函数原型声明中给出;而如果只有函数的定义,或函数定义在前,则缺省形参值需在函数定义中给出。⚫例:intadd(intx=5,inty=6);//原型声明在前intmain(
){add();}intadd(intx,inty){//此处不能再指定缺省值returnx+y;}intadd(intx=5,inty=6){//只有定义,没有原型声明returnx+y;}intmain(){add();}带缺省形参值的函数C++语言程序设计清华大学郑莉2021/7/26
55重载函数的声明⚫C++允许功能相近的函数在相同的作用域内以相同函数名声明,从而形成重载。方便使用,便于记忆。⚫例:形参类型不同intadd(intx,inty);floatadd(floatx,floaty);形参个数不同intadd(intx,i
nty);intadd(intx,inty,intz);函数重载C++语言程序设计清华大学郑莉2021/7/2656注意事项–不要将不同功能的函数声明为重载函数,以免出现调用结果的误解、混淆。这样不好:intadd(intx,inty);intadd(inta,
intb);编译器不以形参名来区分intadd(intx,inty);voidadd(intx,inty);编译器不以返回值来区分intadd(intx,inty){returnx+y;}floatadd(floatx,floaty){returnx-y;}函数重载–重载函数的形参必须不同:
个数不同或类型不同。–编译程序将根据实参和形参的类型及个数的最佳匹配来选择调用哪一个函数。C++语言程序设计清华大学郑莉2021/7/2657例3-16重载函数应用举例编写两个名为sumOfSquare的重载函数,分别求两整数的平方和及两实数的平方和。#include<iostream>us
ingnamespacestd;intsumOfSquare(inta,intb){returna*a+b*b;}doublesumOfSquare(doublea,doubleb){returna*a+b*b;}函数重载intmain(){intm,n;cout<<"E
ntertwointeger:";cin>>m>>n;cout<<"Theirsumofsquare:"<<sumOfSquare(m,n)<<endl;doublex,y;cout<<"Entertworealnumber:";cin>>x>>
y;cout<<"Theirsumofsquare:"<<sumOfSquare(x,y)<<endl;return0;}58运行结果:Entertwointeger:35Theirsumofsquare:34Entertworealnumber:2.35
.8Theirsumofsquare:38.9359C++语言程序设计清华大学郑莉2021/7/2660C++系统函数⚫C++的系统库中提供了几百个函数可供程序员使用。例如:求平方根函数(sprt)、求绝对值函数(abs)等。⚫使用系统
函数时要包含相应的头文件。例如:cmath或math.h使用C++系统函数C++语言程序设计清华大学郑莉2021/7/2661例3-17系统函数应用举例⚫题目:从键盘输入一个角度值,求出该角度的正弦值、余弦值和正切值。⚫分析:系统函数中提供了求正弦值、余弦值和正切值的函数
:sin()、cos()、tan(),函数的说明在头文件cmath中。使用C++系统函数#include<iostream>#include<cmath>usingnamespacestd;constdoublePI=3.14159265358979;intmain
(){doubleangle;cout<<"Pleaseenteranangle:";cin>>angle;//输入角度值doubleradian=angle*PI/180;//转化为弧度值cout<<"sin("<<angle<<"
)="<<sin(radian)<<endl;cout<<"cos("<<angle<<")="<<cos(radian)<<endl;cout<<"tan("<<angle<<")="<<tan(radian)<<endl;return0;}运行结果:3
0sin(30)=0.5cos(30)=0.866025tan(30)=0.5773562C++语言程序设计清华大学郑莉2021/7/2663标准函数与非标准函数⚫标准C++函数–C++标准中规定的函数;–各种编译环境普遍支持,因此用标准函数的程序移植性好;–很多标准C++函数
继承自标准C,头文件以c开头:cmath,cstdlib,cstdio,ctime……⚫非标准C++函数–与特定操作系统或编译环境相关;–在处理和操作系统相关事务时常常需要调用。使用C++系统函数C++语
言程序设计清华大学郑莉2021/7/2664查找系统函数的使用说明⚫查编译系统的库函数手册⚫查联机帮助——VisualC++.NET2008联机帮助的使用方法:进入MSDNLibraryforVisualStudio2008DevelopmentToolsandLan
guages->VisualStudio->VisualC++->Reference->LibrariesReference->Run-TimeLibrary->Run-TimeRoutinesbyCategory使用C++系统函数C++语言程序设计清华大学郑莉2021
/7/2665形参和局部变量的存储⚫为什么不能为形参和局部变量分配固定地址?–他们仅在函数调用时生效,函数返回后即失效,分配固定地址造成空间浪费–更重要的是,发生递归调用时,多次调用间的形参和局部变量应彼此独立⚫需要栈式存储深度探索C++语言程序设计清华大学郑莉2021/7/2666栈⚫栈是
一种容纳数据的容器–数据只能从栈的一端存入(压入栈)–数据只能从栈的同一端取出(弹出栈)深度探索an┆a2a1压入栈弹出栈栈顶栈底C++语言程序设计清华大学郑莉2021/7/2667运行栈⚫运行栈是一段区域的内存空间⚫运行栈分为一个一个栈
帧–每个栈帧对应一次函数调用–栈帧中包括:⚫本次函数调用的形参值⚫控制信息⚫局部变量值⚫一些临时数据–函数调用时,会一个栈帧被压入运行栈–返回时,会有一个栈帧被弹出深度探索C++语言程序设计清华大学郑
莉2021/7/2668运行栈示意图unsignedfac(unsignedn){unsignedf;if(n==0)f=1;elsef=fac(n-1)*n;returnf;}intmain(){unsignedn;cin>>n;unsignedy=fac(n);……}n:0f:
?n:1y:?n:1f:1main()fac(1)fac(0)栈顶深度探索C++语言程序设计清华大学郑莉2021/7/2669函数调用的执行过程⚫栈指针esp:指向运行栈栈顶⚫帧指针ebp:定位形参和局部变量⚫传递参数:调用前把实参压入堆
栈⚫函数调用时的几步关键操作–call指令:将下一条指令地址(返回地址)压入运行栈,转到函数入口地址–被调函数入口处:将当前ebp压入运行栈,用ebp保存esp,调整esp为局部变量留出空间–被调函数出口处:用ebp恢复esp,从运行栈中弹出ebp原值–ret指令
:将返回地址从运行栈弹出,转到返回地址深度探索C++语言程序设计清华大学郑莉2021/7/2670运行栈的数据分布unsignedfac(unsignedn){unsignedf;if(n==0)f=1;
elsef=fac(n-1)*n;returnf;}局部变量febp原值返回地址参数n调用fib(n-1)的参数nespebpfib的栈帧形参和局部变量定位:ebp–8:形参nebp+4:局部变量f深度探索C++语言程序设计清华大学
郑莉2021/7/2671函数声明的意义⚫以错误方式调用函数的危险性–函数的原型信息(参数个数和类型、返回值类型)在编译后即不存在;–如果不要求声明函数,以错误的方式(错误的参数数量或类型)调用函数,会产生不可预
期的结果,但很多情况下不会给出错误提示。⚫函数原型是主调函数与被调函数间的协议⚫运行结果错误vs编译错误–一个错误,与其被淹没在运行中,不如暴露在编译时。深度探索C++语言程序设计清华大学郑莉2021/7/
2672C语言的反例⚫C语言允许–只声明函数名和返回类型,而不声明参数类型–不声明函数,直接调用⚫后果:隐蔽错误–如果给出add()的完整声明,则会自动进行类型转换。⚫声明带来了类型安全doubleadd();intm
ain(){doubles=add(1,2);….return0;}doubleadd(doublea,doubleb){returna+b;}不完整的函数声明错误的调用,压入运行栈的是整数!C++语言程
序设计清华大学郑莉2021/7/2673小结与复习建议⚫主要内容–函数的声明和调用、函数间的参数传递、内联函数、带默认形参值的函数、函数重载、C++系统函数⚫达到的目标–学会将一段功能相对独立的程序写成一个函数,
为下一章学习类和对象打好必要的基础。⚫实验任务–实验三C++语言程序设计清华大学郑莉2021/7/2674