版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1高級語言程序設計第5章函數(shù)與程序結構
(1-3)2本章主要介紹C/C++語言中與函數(shù)和變量相關的知識,討論一些程序整體結構有關的問題。對正確理解C/C++語言/正確書寫C/C++程序都很重要。是學習用C/C++程序設計時應了解的“深層問題”。函數(shù)的定義與使用,函數(shù)原型變量類,作用域與存在期預處理命令,命名空間,多文件項目開發(fā)3第5章函數(shù)與程序結構5.1函數(shù)的定義與調用5.1.1對自定義函數(shù)的需求5.1.2函數(shù)定義5.1.3函數(shù)的調用5.1.4函數(shù)和程序5.1.5局部變量的作用域和生存期5.1.6函數(shù)調用的參數(shù)傳遞機制5.2程序的函數(shù)分解 5.3循環(huán)與遞歸5.4外部變量與靜態(tài)局部變量5.5聲明與定義5.6預處理5.7程序動態(tài)除錯方法(二)4函數(shù)可看作是C/C++語言基本功能的擴充。函數(shù)是特定計算過程的抽象,具有一定通用性,可以按規(guī)定方式對具體數(shù)據(jù)使用。對一個(或一組)具體數(shù)據(jù),函數(shù)執(zhí)行可以計算出一個結果,這個結果可以在后續(xù)計算中使用。函數(shù)的作用是使人可以把一段計算抽象出來,封裝(包裝)起來,使之成為程序中的一個獨立實體。還有為這樣封裝起的代碼取一個名字,做成一個函數(shù)定義(functiondefinition)。當程序中需要做這段計算時,可以通過一種簡潔的形式要求執(zhí)行這段計算,這種片段稱為函數(shù)調用(functioncalling)。5函數(shù)抽象機制的意義:重復片段可用唯一的函數(shù)定義和一些形式簡單的函數(shù)調用取代,使程序更簡短清晰。同樣計算片段只描述一次,易于修改。函數(shù)定義和使用形成對復雜程序的分解。可獨立考慮函數(shù)定義與使用,大大提高工作效率。具有獨立邏輯意義的函數(shù)可看作高層基本操作,使人可以站在合適的抽象層次上觀察把握程序的意義。6圖5-1函數(shù)的調用、執(zhí)行與返回t主調程序被調函數(shù)函數(shù)調用點:控制權轉移到函數(shù),原程序等待函數(shù)執(zhí)行完畢,控制返回主調函數(shù),原程序繼續(xù)
認識函數(shù)調用75.1.1對自定義函數(shù)的需求雖然系統(tǒng)提供了大量的標準庫函數(shù)供用戶使用,但是在實際程序設計中,標準庫函數(shù)還不能滿足用戶的需求,存在著許多對特定函數(shù)的需求,這里舉兩個簡單的例子?!纠?-1】哥德巴赫猜想是數(shù)論中的一個著名的難題,它的陳述為“任一大于2的偶數(shù)都可寫成兩個質數(shù)之和”。這個難題的嚴格證明需要高深的數(shù)學理論,至今還沒有得到徹底解決。請寫程序在小范圍內來驗證這一猜想:對6到200之間的各個偶數(shù)找出一種質數(shù)分解,即找出兩個質數(shù),滿足兩者之和等于這個偶數(shù)。8程序主要部分在主體結構上大致上可以寫成這樣:intmain(){intm,n;for(m=6;m<=2000;m+=2)for(n=3;n<=m/2;n+=2){if(n是質數(shù)
&&m-n是質數(shù)){cout<<m<<"="<<n<<"+"<<m-n<<endl;break;}}return0;}能否把以前寫過的程序片段拿過來使用?9【例5-2】在第4章中有一個簡單猜數(shù)游戲(見“4.4.1編程實例1:一個簡單猜數(shù)游戲”),整個程序的工作流程是簡潔明了的,但是寫出的程序超過了80行,在閱讀源代碼的時候,讀者可能難以把握整個程序的工作流程。如果把整個程序拆分成幾個不同的部分,就能使主程序變得簡潔明了。而且,在程序中輸入最大值和輸入用戶猜測數(shù)據(jù)時,都分別花了多條語句來處理輸入出錯的情形,而這些語句在功能實際上是重復的。10以上例子說明了編程人員有自己定義函數(shù)的需要。也分別說明了需要對自定義函數(shù)需求的兩種場合:1、需要多次重復使用某個計算片段,2、把較長的程序進行合理拆分,從而使主程序變得簡單易讀,方便把握整個程序的工作流程。為了表述簡單,通常把“編程人員在自己的程序中自己定義的函數(shù)”簡稱為“自定義函數(shù)”,并進一步(在不會引起誤解的情形下)簡稱為“函數(shù)”。115.1.2函數(shù)定義要使用自己定義的函數(shù),必須把函數(shù)定義的代碼段包含在整個程序里,這樣的一段代碼稱為一個“函數(shù)定義”。在程序里有了某個函數(shù)的定義后,就可以在程序里任何需要它的地方寫出調用語句來使用它們。用戶可以在程序中自己定義(define)函數(shù),然后就可以在程序中對函數(shù)進行調用(call)。對函數(shù)的定義和調用是互相照應的:在調用時需要按照定義時所規(guī)定的語法形式書寫調用語句,在定義里需要按照調用時所需的功能進行設計。定義(define)調用(call)12聲明參數(shù)的個數(shù)、各參數(shù)的類型和參數(shù)名。參數(shù)名是為了在函數(shù)里使用實際參數(shù)的值。函數(shù)定義的形式函數(shù)定義的形式: 函數(shù)頭部函數(shù)體返回值類型函數(shù)名(參數(shù)表)描述函數(shù)執(zhí)行結束時將會返回的值的類型,也可以是void。用標識符表示,供以后調用這個函數(shù)時使用;英語單詞
void:空的,無人的13函數(shù)定義的形式:函數(shù)頭部
函數(shù)體函數(shù)體(body):用{}包括起來的復合結構。其中定義的變量是本函數(shù)的局部變量。函數(shù)頭部中的參數(shù)也視為局部變量來使用。返回值類型函數(shù)名(參數(shù)表)
{
語句序列;}函數(shù)體里的特殊語句:return(返回)語句。該語句使函數(shù)結束。返回值類型函數(shù)名(參數(shù)表)
{
語句序列;
return
表達式;}用法1:語義:先算表達式,以其值作為函數(shù)返回值。void函數(shù)名(參數(shù)表)
{
語句序列;
return;}用法2:語義:直接從函數(shù)中返回。1415定義函數(shù)時,需要先分析程序中的需求進行設計: 準備拿幾個什么樣的參數(shù)來進行計算? 計算完成之后要返回什么樣的值? 然后給函數(shù)起一個合適的名字。按這樣設計來寫好函數(shù)頭部之后,就可以在函數(shù)體中編寫相應的語句來完成所需的功能。函數(shù)定義的形式:函數(shù)頭部
函數(shù)體返回值類型函數(shù)名(參數(shù)表)
{
語句序列;}16【例5-3】編寫一個函數(shù),用于在給定半徑時計算圓面積。準備拿幾個什么樣的參數(shù)來進行計算?→
doubleradius計算完成之后要返回什么類型的值?→
double然后給函數(shù)起一個合適的名字?!?/p>
scircledoublescircle(doubleradius){//版本1return3.14159265*radius*radius;}doublescircle(doubleradius)
{//版本2doubleerea=3.14159265*radius*radius;returnerea;}函數(shù)頭部中的參數(shù)也視為局部變量來使用函數(shù)體中定義的變量是本函數(shù)的局部變量。17【例5-4】編寫一個函數(shù),用于在給定矩形的長度和寬度時計算矩形面積。doublesrect(doublea,doubleb)
{//兩個參數(shù)returna*b;}【例5-5】編寫一個函數(shù),在屏幕上輸出20個星號并換行。voidprtStar(){//無參數(shù),無返回值cout<<"********************"<<endl;return; //返回(無返回值)}18doublescircle(doubleradius){…}doublesrect(doublea,doubleb)
{…}voidprtStar(){…}上面三個簡單例子,分別說明了單個參數(shù)/多個參數(shù)/無參數(shù)、有返回值/無返回值的函數(shù)的寫法。當然,參數(shù)和返回值的情況可以隨意組合,例如寫出有參數(shù)但無返回值的函數(shù)、或無參數(shù)但是有返回值的函數(shù)。函數(shù)頭部
函數(shù)體195.1.3函數(shù)的調用已經定義好的函數(shù)就可以在程序中進行調用了。在表達式中使用函數(shù)的形式是:先寫函數(shù)名,然后寫一對圓括號(無參函數(shù)也需要寫),再根據(jù)函數(shù)定義時的函數(shù)頭部中所規(guī)定的參數(shù)類型和參數(shù)個數(shù)寫上單個/多個表達式(用逗號隔開)。這些表達式是送給函數(shù)作為計算對象的,稱為函數(shù)的實際參數(shù),簡稱實參。所以,函數(shù)調用的一般形式為:函數(shù)名(實際參數(shù))函數(shù)名(實際參數(shù),實際參數(shù))函數(shù)名()……20函數(shù)調用:函數(shù)名(實際參數(shù)表)多個實參用逗號分隔。在調用時,把實參的值傳遞給形參。函數(shù)體的復合語句在參數(shù)具有特定實參值的情況下開始執(zhí)行。函數(shù)定義:返回值類型函數(shù)名(參數(shù)表)
{語句序列;}函數(shù)定義中,參數(shù)表中的參數(shù)稱為形參。21在編寫調用語句時,應該根據(jù)函數(shù)定義時的函數(shù)頭部中的參數(shù)表和返回值進行相應的書寫:1、參數(shù)表非空,則調用時必須提供個數(shù)正確、類型合適的實參。實參是具體函數(shù)計算的出發(fā)點。實參可以是數(shù)值、變量或由數(shù)值和變量構成的表達式。如果函數(shù)的參數(shù)表為空,那么就不需要(而且也不允許)提供參數(shù),只需要寫一對空的圓括號(不可省略)。2、如果提供的實參類型與形參類型不一致,那么在執(zhí)行時就會發(fā)生類型轉換。223、對于具有返回值的函數(shù),在其中執(zhí)行到某一條return表達式;
語句時,該語句中的表達式的值被計算出來并作為該函數(shù)的返回值,這個返回值可供調用點處后續(xù)使用(也可以閑棄不用)。所以,有返回值的函數(shù)一般出現(xiàn)在表達式里,用其返回值參與后續(xù)操作(例如給其它變量賦值,或參與后續(xù)計算,或者直接打印輸出)。無返回值的函數(shù)在執(zhí)行結束時沒有任何值可供調用處使用,顯然不能放在表達式里使用,即不能用于做賦值、計算或打印之類的操作。23例如,對于scircle和srect函數(shù)(它們分別需要1個和2個參數(shù),都有double類型的返回值),可以寫出如下的調用語句:doubles;s=scircle(2.4);//直接提供數(shù)值作為參數(shù),返回值用于賦值;s=scircle(2.4+sin(1.57));//含有數(shù)學函數(shù)的表達式作為參數(shù),返回值用于賦值;cout<<scircle(1.5+2.4);//算術表達式作為參數(shù),返回值用于打印輸出;doubler=1.5;s=scircle(r);//變量作為參數(shù),返回值用于賦值;cout<<scircle(r*2);//算術表達式作為參數(shù),返回值用于打印輸出doublelength=3.5,width=4.2;s=srect(3.5,4.2);//直接提供數(shù)值作為參數(shù),返回值用于賦值;s=srect(3*sin(2.),2*cos(5.2));//以表達式作為參數(shù),返回值用于賦值;s=srect(length,width);//變量作為參數(shù),返回值用于賦值;cout<<srect(length,width);//變量作為參數(shù),返回值用于打印輸出24而對于prtStar函數(shù),由于它不需要參數(shù),所以在調用時就不需要提供參數(shù),可以這樣調用(注意,小括號不能省略):prtStar();不需要(不允許)提供參數(shù)給它:prtStar(100);//wrong!沒有返回值可供用于賦值或打印:s=prtStar();//wrong!cout<<prtStar();//wrong!25【例5-6】把前文的幾個示例函數(shù)寫在同一個程序文件中,并寫一個main函數(shù),在其中調用這些函數(shù)。#include<iostream>#include<cmath>usingnamespacestd;//doublescircle(doubleradius){//計算圓面積函數(shù)之版本1//return3.14159265*radius*radius;//}doublescircle(doubleradius){//計算圓面積函數(shù)之版本2doubleerea=3.14159265*radius*radius;returnerea;}26doublesrect(doublea,doubleb){returna*b;}voidprtStar(){cout<<"********************"<<endl;return;}intmain(){doubles;prtStar();s=scircle(2.4);cout<<"s="<<s<<endl;s=scircle(2.4+sin(1.57));cout<<"s="<<s<<endl;cout<<scircle(1.5+2.4)<<endl;注意:1、每一個函數(shù)都是獨立的。不能把一個函數(shù)定義寫在另一個函數(shù)定義的內部。2、自定義的函數(shù)要寫在main函數(shù)上方;3、函數(shù)之間要寫適當?shù)目招校逦烙^。(函數(shù)內部也可以寫適當?shù)目招校?7doubler=1.5;s=scircle(r);cout<<"s="<<s<<endl;cout<<scircle(r*2)<<endl;prtStar();s=srect(3.5,4.2);cout<<"s="<<s<<endl;s=srect(3*sin(2.),2*cos(5.2));cout<<"s="<<s<<endl;doublelength=3.5,width=4.2;s=srect(length,width);cout<<"s="<<s<<endl;cout<<"s="<<srect(length,width)<<endl;prtStar();return0;}這個例題包含了很多知識,下面逐一展開介紹。5.1.4函數(shù)和程序5.1.5局部變量的作用域和生存期5.1.6函數(shù)調用的參數(shù)傳遞機制28295.1.4函數(shù)和程序一個完整的程序,必須有且僅有一個名為main的函數(shù)(主函數(shù))。intmain(){
…… return0;}函數(shù)main表示程序的執(zhí)行過程。程序從main的體開始執(zhí)行,直到該復合結構結束。其他函數(shù)不經調用就不會執(zhí)行。main在程序啟動時被自動調用(由運行系統(tǒng)調用)。程序里不允許調用main。在書寫的形式上,一個程序文件中的每個函數(shù)都是平等的,彼此不能包含,不能把一個函數(shù)的定義寫在另一個函數(shù)內部。在習慣上,人們常把自定義的函數(shù)寫在前面,把main函數(shù)寫在最后。這是為了滿足對函數(shù)的“先定義后使用”規(guī)則。在一個程序中,不允許出現(xiàn)多個自定義函數(shù)具有相同的返回值類型、函數(shù)名和參數(shù)表的情況。30315.1.5局部變量的作用域和生存期一個變量定義,是定義了一個具有特定類型的變量,并給變量命名。同時,一個變量定義還確定了兩個問題:1、在程序中的哪個范圍內該變量定義有效。每個變量都有一個確定的作用范圍,稱為該變量的作用域(scope),變量的作用域由變量定義的位置確定。2、變量的實現(xiàn)基礎是內存單元,變量在程序運行中建立,并在某個時間撤消。一個變量在程序執(zhí)行中從建立到撤消的存在時期稱為這個變量的生存期(lifetime)或存在期。32作用域和生存期是程序語言中的兩個重要概念,弄清楚它們,許多問題就容易理解了。作用域和生存期有聯(lián)系但又不同,這兩個概念是分別從空間和時間的角度來體現(xiàn)變量的特性。作用域講變量定義的作用范圍,說的是源程序中的一段范圍,可以在代碼中劃清楚,是靜態(tài)概念。生存期則完全是動態(tài)概念,講的是程序執(zhí)行過程中的一段期間。變量在生存期里一直保持著自己的存儲單元,保存于這些存儲單元中的值在被賦新值之前會一直保持不變。33在C/C++程序中的任何復合語句里的任何位置都可以定義變量。在一個復合結構里定義的變量可以在該復合結構的內部使用。從作用域的角度來看,在這些語句中所定義的變量只能在相應的局部范圍內使用。它們的作用域是從該變量定義的語句開始,到復合語句結束為止,在這個復合語句之外該定義無效。因此,這些變量被稱為局部變量(Localvariables)。函數(shù)形參都看作函數(shù)定義的局部變量,其作用域就是這個函數(shù)的函數(shù)體。for語句的小括號中定義的變量,作用域就是整個for語句。34#include<iostream>usingnamespacestd;//doublescircle(doubleradius){//return3.14159265*radius*radius;//}doublescircle(doubleradius){doubleerea=3.14159265*radius*radius;returnerea;}doublesrect(doublea,doubleb){returna*b;}voidprtStar(){cout<<"********************"<<endl;return;}35intmain(){doubles;prtStar();
s=scircle(2.4);cout<<"s="<<s<<endl;doubler=1.5;
s=scircle(r);cout<<"s="<<s<<endl;cout<<scircle(r*2)<<endl;doublelength=3.5,width=4.2;s=srect(length,width);cout<<"s="<<srect(length,width)<<endl;return0;}“函數(shù)調用的參數(shù)傳遞機制”見下一節(jié)。36不同作用域內的變量名是否允許同名呢?語言對此有如下規(guī)定:(1)同一作用域里不允許定義同名變量:作用域相同的變量的名字不能沖突。否則使用哪個變量的問題就無法確定了。(2)不同作用域容許定義同名變量。也是人們經常做的。37【例5-7】寫一個函數(shù)求整數(shù)平方和,然后在main函數(shù)中調用這個函數(shù)求出給定m的值。intsumsq(intm){intsum=0;for(intn=0;n<m;n++){intk=n*n;sum=sum+k;cout<<"n="<<n<<"sum="<<sum<<endl;}cout<<"m="<<m<<"sum="<<sum<<endl;returnsum;}intmain(){intm;cout<<"inputaninteger:";cin>>m;cout<<"sum="<<sumsq(m)<<endl;return0;}局部變量的作用域,是從該變量定義的語句開始,到復合語句結束為止。for語句的小括號中定義的變量的作用域就是整個for語句。函數(shù)形參的作用域是這個函數(shù)的函數(shù)體。38由于在函數(shù)體內可以嵌套其它復合語句,因此產生了作用域的嵌套,在這些嵌套的作用域中是否可以使用同名變量呢?這時,這兩個同名變量雖然同名但作用域不同,所以互不相干,故仍然服從上面第二條規(guī)定:不同作用域中容許定義同名變量。但此時有一個新問題:使用該變量名時到底是在使用哪一個變量?語言對此還有一條規(guī)定:(3)當內層復合語句出現(xiàn)同名變量定義時,外層同名定義將被內層定義遮蔽。也就是說,在使用該變量名時,實際上是優(yōu)先使用內層定義的變量。39例如,本例的sumsq函數(shù)中,把變量
“k”的名字寫成
“m”也可以:intsumsq(intm){//正確而讓人難懂的版本
intsum=0;for(intn=0;n<m;n++){intm=n*n;//定義了同名變量msum=sum+m;//使用內層變量mcout<<"n="<<n<<"sum="<<sum<<endl;}cout<<"m="<<m<<"sum="<<sum<<endl;//使用形參mreturnsum;}這個函數(shù)是正確的,但是寫成這樣顯然很讓人難以讀懂!因此,本書作者覺得對讀者有用的建議是:在程序中盡量避免在嵌套的作用域中出現(xiàn)同名變量!40有時候也需要注意語句的寫法有誤而導致出現(xiàn)嵌套的變量遮蔽現(xiàn)象。例如下面的sumsq函數(shù)就含有功能性錯誤:intsumsq(intm){//含有錯誤的版本
intsum;//定義了函數(shù)內的局部變量sumfor(intn=0,sum=0;n<=m;n++){//定義了內層局部變量n和sumintk=n*n;sum=sum+k;cout<<"n="<<n<<"sum="<<sum<<endl;}cout<<"m="<<m<<"sum="<<sum<<endl;returnsum;}這個函數(shù)的錯誤原因是:……41上面詳細介紹了變量的作用域,下面介紹變量的存在期。在復合語句里定義的局部變量的存在期,就是這個復合語句的執(zhí)行期間。也就是說,該復合語句開始執(zhí)行時建立這里面定義的所有變量。它們一直存在到該復合語句結束。復合語句結束時,內部定義的所有變量都撤消。如果執(zhí)行再進入這一復合語句,那么就再次建立這些變量。新建變量與上次執(zhí)行建立的變量毫無關系,是另一組變量。正是由于在復合語句里定義的變量被自動建立和撤消的性質,語言中也把它們稱作自動變量。這幾句話很簡單,但含義很深刻。42以sumsq函數(shù)為例說明:intsumsq(intm){intsum=0;for(intn=0;n<=m;n++){intk=n*n;//for結構每次執(zhí)行循環(huán)體時新建變量ksum=sum+k;cout<<"n="<<n<<"sum="<<sum<<endl;}//for結構每次循環(huán)體執(zhí)行結束時銷毀變量k//for結構執(zhí)行結束時銷毀新建變量ncout<<"m="<<m<<"sum="<<sum<<endl;returnsum;}for結構開始執(zhí)行時新建變量n43在編程時應當注意變量的作用域和存在期,從而理解它們在特定執(zhí)行環(huán)境中的值。如果不能正確理解這一點,則所寫的程序可能會含有非常隱蔽的錯誤。假設有下面程序片段:for(intn=1;n<10;n++){intk;if(n==1)k=5;k=k+n;//循環(huán)執(zhí)行第二次到達這里時k的值無法確定
cout<<"k="<<k<<endl;}每次循環(huán)體開始執(zhí)行時建立一個名為k的新變量(系統(tǒng)為它分配存儲空間)。第一次循環(huán)時,由于n值為1,k賦值5,執(zhí)行k=k+n;之后,k的值為6,循環(huán)結束時變量k被撤消。但在第二次及其后的循環(huán)執(zhí)行中條件不成立,相應賦值語句不執(zhí)行,這樣到了k=k+n;這一句時,新建立的變量k未經過賦值,值不能確定。所以這個程序中含有錯誤。44讀者如果多次運行這個程序,通常會發(fā)現(xiàn)所輸出的結果是一模一樣的,最后輸出的結果在數(shù)學上也是正確的(“k=50”),好像這個程序并不含有錯誤似的。這是因為系統(tǒng)在多次新建變量時,偶然地選用了上一次的內存空間,上一次的殘留值就被用作初始值來使用。如果系統(tǒng)工作繁忙,內存空間的存儲情況頻繁地發(fā)生變化,那么此程序運行再次進入循環(huán)并新建變量k時,很可能該變量所分配的內存空間與上一次并不相同,就會得到一個無法預料的值作為初始值(用于執(zhí)行k=k+n;語句)。所得的計算結果就很可能完全無法預料了?!虼耍厦娴某绦蚱沃写_實含有非常隱蔽的錯誤。定義在復合結構中的變量,其作用域是局部的,所以稱為局部變量。它們在程序執(zhí)行期間是自動建立和銷毀的,所以從生存期的角度來說,稱為自動變量。45465.1.6函數(shù)調用的參數(shù)傳遞機制在調用函數(shù)時,實參與形參具體是什么樣的關系?當實參是變量時,在函數(shù)體內改變之后的形參值是否會返回給實參呢?這就需要我們理解C和C++語言中的參數(shù)機制。doublesrect(doublea,doubleb){returna*b;}intmain(){doublelength=3.5,width=4.2,s;s=srect(length,width);cout<<"s="<<s<<endl;return0;}47函數(shù)調用與參數(shù)值的傳遞
nm把實參值復制給形參函數(shù)func的內部abC和C++語言中的函數(shù)的基本參數(shù)機制是值參數(shù):函數(shù)調用時先計算實參表達式的值,把值復制給對應形參,而后執(zhí)行函數(shù)體。函數(shù)內對形參的操作與實參無關。函數(shù)內對形參的賦值與實參無關。函數(shù)定義:intfunc(inta,intb){......}函數(shù)調用:cout<<func(m,n);函數(shù)func的調用環(huán)境返回值return表達式;48【例5-8】:#include<iostream>usingnamespacestd;voidswap(inta,intb){ intk=a;a=b;b=k; cout<<"swap:a="<<a<<",b="<<b<<endl;;return;}intmain(){ intm=10,n=25; cout<<"before:m="<<m<<",n="<<n<<endl;
swap(m,n); cout<<"after:m="<<m<<",n="<<n<<endl;}輸出結果(a和b的值在調用前后并未改變):before:m=10,n=25swap:a=25,b=10after:m=10,n=25請仔細體會函數(shù)的參數(shù)值傳遞機制!49C++中的引用型參數(shù)C++語言中添加了名為“引用(reference)”的新特性,使用這種方式在函數(shù)之間傳遞參數(shù),調用時在函數(shù)中直接對實參進行操作,可改變調用處的變量的值。書寫形式上是在形參名稱前面加上
&
字符。nm調用時直接對實參進行操作函數(shù)swapref的內部&a&b函數(shù)定義:voidswapref(int&a,int&b){......}函數(shù)調用:swapref(m,n);函數(shù)swapref的調用環(huán)境50舉例:voidswapref(int&a,int&b){//形參a,b都是引用
intk=a;a=b;b=k;
cout<<"swapedinside:a="<<a<<"b="<<b<<
endl;return;}intmain(){ intm=10,n=25;cout<<"beforeswapref:m="<<m<<"n="<<n<<endl;
swapref(m,n);cout<<"afterswapref:m="<<m<<"n="<<n<<endl;}輸出結果(變量m和n的值在調用前后發(fā)生改變):beforeswapref:m=10,n=25swapedinside:a=25,b=10afterswapref:m=25,n=1051對比swap和swapref兩個函數(shù),并對比兩個main函數(shù),可以看到差別僅僅是:在swapref的函數(shù)頭部的參數(shù)列表中,在形參前面加上“&”字符。這樣微小的書寫差別產生了巨大的語義差別:調用函數(shù)時不再是使用傳統(tǒng)的值傳遞機制了!在調用時不再是把實參的值復制給形參,而是把形參作為實參的別名,直接對實參進行操作。voidswapref(int&a,int&b){…}voidswap(inta,intb){…}52注意:調用含有引用型參數(shù)的函數(shù)時,對引用型參數(shù)必須提供一個變量作為實參,而不能以常量或含有運算符的表達式作為實參。正確:swapref(m,n);//m和n都是變量錯誤:swapref(10,25);//用常量做實參swapref(m,m+12);//用含有運算符的表達式做實參voidswapref(int&a,int&b){…}小練習:下面程序的運行結果如何?#include<iostream>usingnamespacestd;voidmyswap(int&a,intb){//a是引用型參數(shù),b是值參數(shù) intk=a;a=b;b=k;}intmain(){ intm=10,n=25;
myswap(m,n);}5354最后簡單地介紹常參數(shù)。與常變量類似,函數(shù)也可以有常參數(shù)。常參數(shù)同樣由實參提供初值,但在函數(shù)體里不允許對它們重新賦值。常參數(shù)的定義形式是在參數(shù)的類型描述前加const關鍵字:intfunc1(constinta,intb){......}//常值參數(shù)intfunc2(constint&a,intb){......}//常引用參數(shù)參數(shù)a被指定為常參數(shù),因此在函數(shù)體內不允許對它重新賦值。如果在函數(shù)體寫了任何對a重新賦值的語句,則編譯時就會報錯。小結(5.1函數(shù)的定義與調用)對自定義函數(shù)的需求:1、重復使用的代碼片段;2、長程序拆分。函數(shù)定義:返回值類型
函數(shù)名(參數(shù)表)
{...}
函數(shù)頭部
函數(shù)體編寫函數(shù)時,先根據(jù)需求設計好參數(shù)表和返回值類型,并給函數(shù)命名。然后再寫函數(shù)體。函數(shù)調用:函數(shù)名(實參列表)返回值類型為void的函數(shù)沒有返回值可用。程序中必須有而且只能有一個main函數(shù)。程序總是從main函數(shù)開始執(zhí)行。其它函數(shù)未經調用就不會執(zhí)行。不能把一個函數(shù)的定義寫在另一個函數(shù)內部。通常把自定義的函數(shù)寫在前面,把main函數(shù)寫在最后。55續(xù)變量的作用域由變量定義的位置確定。局部變量作用域是從該變量定義的語句開始,到復合語句結束為止。函數(shù)的形參可當作局部變量使用,性質與局部變量相同。當內層復合語句出現(xiàn)同名變量定義時,外層同名定義將被內層定義遮蔽。局部變量的存在期,就是所在的復合語句的執(zhí)行期間。自動建立和銷毀,也稱為自動變量。值參數(shù)機制:函數(shù)調用時先計算實參表達式的值,把值復制給對應形參,而后執(zhí)行函數(shù)體。C++
的引用參數(shù):函數(shù)調用時在函數(shù)中直接對實參進行操作,可改變調用處的變量的值。必須提供變量作為實參。函數(shù)也可以有常參數(shù)。在前面加const關鍵詞。56續(xù)57第5章函數(shù)與程序結構5.1函數(shù)的定義與調用5.2程序的函數(shù)分解 5.3循環(huán)與遞歸5.4外部變量與靜態(tài)局部變量5.5聲明與定義5.6預處理5.7程序動態(tài)除錯方法(二)585.2程序的函數(shù)分解5.2.1程序的函數(shù)分解什么樣的程序片段應該定義為函數(shù):重復出現(xiàn)的相同/相似計算片段,可設法抽取共同性的東西,定義為函數(shù)。長計算過程中有邏輯獨立性的片段,即使出現(xiàn)一次也可定義為函數(shù),以分解復雜性。經驗原則:可以定義為函數(shù)的東西,就應該定義為函數(shù);一個函數(shù)一般不超過一頁。往往存在很多可行的分解,尋找合理有效的分解是需要學習的東西。59函數(shù)外部關心的是函數(shù)如何使用:—函數(shù)實現(xiàn)什么功能—函數(shù)的名字是什么—函數(shù)有幾個參數(shù),類型是什么—函數(shù)返回什么值…
…
函數(shù)內部關心的是函數(shù)應當如何實現(xiàn)—采用什么計算方法—采用什么程序結構—怎樣得到計算結果…
…
函數(shù)頭部的說明
5.2.2函數(shù)封裝和兩種視角函數(shù)定義(封裝)把函數(shù)內外隔成兩個世界。不同世界形成了對函數(shù)的兩種觀點。函數(shù)頭部規(guī)定了兩個世界的交流方式。60函數(shù)是獨立的邏輯實體。定義后可以調用執(zhí)行。由此形成對函數(shù)的兩種觀察方式:1)從函數(shù)外(以函數(shù)使用者的角度)看函數(shù);2)在函數(shù)內(以函數(shù)定義者的角度)看函數(shù)。計劃函數(shù)時,要同時從兩個觀點看:需要什么函數(shù)/參數(shù)/返回值?分析確定函數(shù)頭部,定好公共規(guī)范。編寫函數(shù)定義時應站在內部觀點思考/解決問題;調用函數(shù)時應站在外部立場上思考/解決問題。功能描述清楚,接口定義好以后,函數(shù)定義和使用可由不同人做。要求雙方遵循共同規(guī)范,對函數(shù)功能有一致理解。自己寫函數(shù)時也要保證兩種觀點的一致性。615.2.3自定義函數(shù)示例舉例讓讀者體會如何編寫自定義函數(shù)和如何進行函數(shù)分解。希望通過這些例題說明在使用函數(shù)進行編程的一般技巧。讀者也可以從中體會到函數(shù)分解的一般性經驗?!纠?-10】以迭代公式求x的立方根【例5-11】寫函數(shù)求sinx的近似值【例5-12】寫函數(shù)判斷變量year的值是否閏年【例5-13】寫謂詞函數(shù)判斷質數(shù)【例5-14】用函數(shù)驗證歌德巴赫猜想【例5-15】歌德巴赫(Goldbach)猜想+62【例5-10】求x立方根的迭代公式是,寫一個函數(shù),從鍵盤上輸入x值,然后利用這個公式求x的立方根的近似值,要求達到精度。解:把前文例題求出立方根的程序修改為自定義函數(shù)。根據(jù)“cubicroot”把函數(shù)命名為cbrt,函數(shù)參數(shù)是一個double類型的數(shù)據(jù),函數(shù)返回值即為求出的立方根。doublecbrt(doublex)
{ if(x==0)
return0;//計算出0的立方根為0作為函數(shù)返回值
doublex1,x2=x; do{ x1=x2; x2=(2.0*x1+x/(x1*x1))/3.0; //cout<<x2<<endl; }while(fabs((x2-x1)/x1)>=1E-6);
returnx2; //計算得到滿足精度的項作為函數(shù)返回值}63寫一個main函數(shù)調用cbrt函數(shù)進行測試:intmain(){//測試cbrtdoublex;cout<<"Inputxtotestcbrt(Ctrl-ztoend)"<<endl;while((cin>>x)) cout<<"cbrt="<<cbrt(x)<<endl;cout<<"testfinished."<<endl;
return0;}運行時選擇合理的測試數(shù)據(jù):0值/非0值,±1,±8,±27,±1000,±1000000前文使用單個main函數(shù)的程序:intmain(){doublex,x1,x2;cout<<"Pleaseinputx:";cin>>x;if(x==0){cout<<"cubicrootof"<<x<<"is:"<<x2;return0;//程序結束,返回值為0}
x2=x;do{x1=x2;x2=(2.0*x1+x/(x1*x1))/3.0;cout<<x2<<endl;}while(fabs((x2-x1)/x1)>=1E-6);cout<<"cubicrootofxis:"<<x2<<endl;return0;}64這里進行函數(shù)分解后寫出的程序:doublecbrt(doublex)
{ if(x==0)
return0;//0的立方根作為函數(shù)返回值
doublex1,x2=x; do{ x1=x2; x2=(2.0*x1+x/(x1*x1))/3.0; //cout<<x2<<endl; }while(fabs((x2-x1)/x1)>=1E-6);
returnx2; //函數(shù)返回值}intmain(){//測試cbrtdoublex;cout<<"Inputxtotestcbrt(Ctrl-ztoend)"<<endl;while((cin>>x)) cout<<"cbrt="<<cbrt(x)<<endl;cout<<"testfinished."<<endl;return0;}對比兩種寫法的差異:把相關的計算代碼封裝起來構成函數(shù),并合理地設置形參和局部變量;函數(shù)中用形參接收用于計算的參數(shù),用return返回計算結果;函數(shù)中不要輸出信息,而是由調用它的主函數(shù)輸出信息。65【例5-11】寫一個函數(shù)利用公式求出sinx的近似值(要求累加項的值小于10?6),并與標準庫中的sin函數(shù)的計算結果進行比較。把前文例題的源代碼修改為自定義函數(shù)dsin:doubledsin(doublex)
{ //x=fmod(x,2*3.1415926); //此句有何作用?
doublesum=0.0,t=x; intn=0; while(t>=1E-7||t<=-1E-7){ sum=sum+t; n=n+1; t=-t*x*x/(2*n)/(2*n+1);
//cout<<"n="<<n<<"t="<<t<<"sum="<<sum<<endl; }
returnsum;}66前文例題給出的源代碼:intmain(){ doublex,sum,t; cout<<"Pleaseinputx:"; cin>>x; x=fmod(x,2*3.1415927); sum=0.0; t=x; intn=0; while(t>=1E-6||t<=-1E-6){ sum=sum+t; n=n+1; t=-t*x*x/(2*n)/(2*n+1); cout<<"n="<<n<<"t="<<t<<"sum="<<sum<<endl; } cout<<"mysin"<<x<<"is:"<<sum<<endl; cout<<"standardsin"<<x<<"is:"<<sin(x); return0;}把這一部分代碼封裝成函數(shù)定義,用于計算的變量x做成形參,最后返回計算結果(變量sum)。doubledsin(doublex)
{//x=fmod(x,2*3.1415926);doublesum=0.0,t=x;intn=0;while(t>=1E-7||t<=-1E-7){sum=sum+t;n=n+1;t=-t*x*x/(2*n)/(2*n+1);
//cout<<"n="<<n<<"t="<<t<<"sum="<<sum<<endl;}returnsum;}67寫一個main函數(shù)調用dsin函數(shù)進行測試:intmain(){//測試dsin函數(shù)。通過循環(huán)提供參數(shù)自動測試 doublex; cout<<"testdsin:\nx\tdsin(x)\tsin(x)\n";
for(inti=-100;i<=1000;i+=10){ //cout<<"Pleaseinputx:"; //cin>>x; x=i; cout<<x<<'\t'<<dsin(x)<<'\t'<<sin(x)<<'\t'; cout<<dsin(x)-sin(x)<<endl; } return0;}要查看此程序運行結果!是否滿足要求?如果出現(xiàn)偏差該如何解決?總結上面兩個例題:在編程實踐中,在按照要求寫一個函數(shù)來實現(xiàn)某項功能時,通常都需要在寫完該函數(shù)之后,再寫一個main函數(shù),測試所寫的函數(shù)是否能正常工作。測試時可以循環(huán)地用手工輸入一批數(shù)據(jù)作為參數(shù),或者用一個循環(huán)自動地提供一批參數(shù)。6869【例5-12】寫一個函數(shù)判斷變量year的值是否表示一個閏年的年份,然后在main函數(shù)中調用這個函數(shù),打印輸出1900-2100中的閏年。#include<iostream>usingnamespacestd;intisleapyear(intyear){
return((year%4==0&&year%100!=0)||year%400==0);}intmain(){ for(intyear=1900;year<=2100;year++) if(isleapyear(year)) cout<<year<<""; return0;}注意,這是兩個不同的變量70【例5-13】寫一個謂詞函數(shù),判斷一個整數(shù)(參數(shù))是否為質數(shù)。然后在main函數(shù)中判斷并輸出-10--999中的質數(shù)。函數(shù)命名為isprime,注意把n<=1時判斷為非質數(shù)。把原有例題源程序改寫為謂詞函數(shù)isprime:intisprime(intn){ //版本1 if(n<=1)//n<=1時判斷為非質數(shù) return0; //n>1時繼續(xù)分析判斷 intk; for(intk=2;k*k<=n;k++) if(n%k==0) //發(fā)現(xiàn)一個因數(shù)就退出循環(huán)
break; //根據(jù)循環(huán)退出或結束的情形來判斷
return(k*k<=n&&n%k==0)?0:1;}71函數(shù)可以更巧妙地使用return語句而寫得更簡潔:intisprime(intn){ //版本2 if(n<=1)return0; //非質數(shù) for(intk=2;k*k<=n;k++) if(n%k==0)//發(fā)現(xiàn)一個因數(shù)就足以判斷不是質數(shù)
return0;
return1;//上面循環(huán)中沒有發(fā)現(xiàn)因數(shù),所以判斷是質數(shù)}72有了上面的isprime函數(shù),可以寫出main函數(shù)如下:intmain(){ //輸出-10--999之間的所有質數(shù)
for(intn=-10;n<999;n++) if(isprime(n)) cout<<n<<""; return0;}可以把isprime函數(shù)和main函數(shù)拼裝成一個完整的程序。(要加上必要的其它內容;isprime函數(shù)的兩個版本只能任選其一)注意在函數(shù)中對n<=1進行了特殊處理:
if(n<=1)return0;由此可見,在寫一個程序(或函數(shù))之前,首先應該仔細分析需要考慮的情況。完成之后還應該仔細檢查,看看是否有什么遺漏。如果事先分析周全,應該能看到這些問題。73從isleapyear和isprime這兩個函數(shù)可以注意到,謂詞函數(shù)通常只負責進行某種判斷并返回判斷結果,不進行信息輸出。而由調用這類函數(shù)的程序根據(jù)自身需求進行信息輸出。這是一種合理的函數(shù)功能分解方式。intisprime(intn){ //版本2 if(n<=1)return0; //非質數(shù) for(intk=2;k*k<=n;k++) if(n%k==0)//發(fā)現(xiàn)一個因數(shù)就足以判斷不是質數(shù)
return0;
return1;//上面循環(huán)中沒有發(fā)現(xiàn)因數(shù),所以判斷是質數(shù)}intisleapyear(intyear){
return((year%4==0&&year%100!=0)||year%400==0);}74【例5-14】回到例5-1,使用已有的isprime函數(shù)在小范圍內驗證歌德巴赫猜想:對6到200之間的各偶數(shù)找出一種質數(shù)分解,即找出兩個質數(shù),使它們的和等于這個偶數(shù)。把isprime函數(shù)插入已寫出的程序主體結構:intmain(){ intm,n; for(m=6;m<=200;m+=2) for(n=3;n<=m/2;n+=2){
//if(n是質數(shù)
&&m-n是質數(shù))
if(isprime(n)&&isprime(m-n)){ cout<<m<<"="<<n<<"+"<<m-n<<endl; break; } } return0;}請讀者把isprime函數(shù)和這個main函數(shù)拼裝成一個完整的程序文件。75【例5-15】歌德巴赫(Goldbach)猜想“任一大于2的偶數(shù)都可寫成兩個質數(shù)之和”對于在計算機上已驗證過的偶數(shù)都是成立的,而且很多偶數(shù)有多種分解方式,那么,對給定的偶數(shù)是否能找到一種分解方式,使分解得的兩個質數(shù)之差小于該偶數(shù)的1/4?請寫一個函數(shù)對給定的偶數(shù)尋找兩個質數(shù)之差最小的分解方式,并把這兩個質數(shù)返回到主調函數(shù);而且如果兩個質數(shù)之差小于該偶數(shù)的1/4,則函數(shù)的返回值不為0,表示成功;如果兩個質數(shù)之差大于該偶數(shù)的1/4,則函數(shù)的返回值為0,表示失敗。然后再寫一個主函數(shù),對6~200中的偶數(shù)進行驗證是否可以這樣分解。76尋找把偶數(shù)分解成兩個質數(shù)之和,可以借鑒上一例題。尋找“兩個質數(shù)之差最小”的分解方式,只要從該偶數(shù)的1/2(準確地說是從“等于或大于該偶數(shù)的1/2的第一個奇數(shù)”)開始往上搜索即可,所找到的第一種分解方式就滿足“兩個質數(shù)之差最小”。題目中要求把給定的偶數(shù)進行分解成質數(shù)并返回“兩個質數(shù)之差小于該偶數(shù)的1/4”的判斷結果,對此函數(shù)可以有多種設計方案。先來看一種最直觀的設計方案:77(1)題目要求把給定的偶數(shù)分解成兩個質數(shù),可以把分解得的兩個質數(shù)返回到主調函數(shù),因此待編寫的函數(shù)的形參設定為三個整數(shù),用于表示待分解的偶數(shù)和分解而得的兩個質數(shù),而且分解而得的兩個質數(shù)需要返回到主調函數(shù)中,所以這兩個形參需要作為
引用型參數(shù)。(2)根據(jù)題目要求,顯然要求函數(shù)的返回值是整數(shù)。(3)按照見名識義的原則,把函數(shù)命名為“goldbach”。按照這一設計方案,寫出函數(shù)頭部如下:intgoldbach(intn,int&k1,int&k2)用于分解的偶數(shù)兩個引用參數(shù),表示進行分解后得到的兩個質數(shù)78intgoldbach(intn,int&k1,int&k2){if(n%2==1||n<6)//奇數(shù)或小于6的偶數(shù)不能分解
return0;
k1=(n/2)%2?n/2:n/2+1;//等于或大于該偶數(shù)的1/2的第一個奇數(shù)
for(k2=n-k1;k1<=n;k1+=2,k2=n-k1)if(isprime(k1)&&isprime(k2))
return(k1-k2<n/4?1:0); return0;}intmain(){intm,m1,m2,found;for(m=6;m<=200;m+=2){
found=goldbach(m,m1,m2);cout<<m<<"="<<m1<<"+"<<m2<<"\t";cout<<(found?"Yes":"NO")<<"\t"<<m1-m2<<endl;}return0;}用函數(shù)的返回值表示函數(shù)的工作狀態(tài)79另一些可行的其它函數(shù)設計方案并編寫程序——當然每一種函數(shù)設計方案都需要相應地考慮調用時如何處理:(1)把形參1也寫成引用:
intgoldbach1(int&n,int&k1,int&k2);
這種寫法也可以。但在調用時必須對n提供一個變量作為實參,而不能直接寫某個常數(shù):
goldbach(1000,m1,m2);//okgoldbach1(1000,m1,m2);//wrong!(2)只用&k1就夠了,不用&k2:
intgoldbach2(intn,int&k1);
這種寫法也可以。但在主函數(shù)中調用后需要另行計算出第2個質數(shù),如下所示:
found=goldbach2(m,m1);cout<<m<<"="<<m1<<"+"<<m-m1<<"\t";80(3)不用引用,直接拿函數(shù)返回值表示一個質數(shù)(值為0時表示分解不成功):
intgoldbach3(intn);
這種寫法也可以。但在調用時需要另想辦法處理這兩個質數(shù),例如寫成這樣:
m1=goldbach3(m);cout<<m<<"="<<m1<<"+"<<m-m1<<"\t";cout<<(m1?"Yes":"NO")<<endl;81由上例可知,同對一個問題,通??梢栽O計出多種函數(shù)設計方案來編程解答,每一種函數(shù)所需的參數(shù)、參數(shù)所代表的數(shù)據(jù)含義可以各不相同,相應地也需要在調用時加以考慮如何提供參數(shù)和使用函數(shù)返回值(或可以返回數(shù)據(jù)的參數(shù))。因此,讀者應該理解,各種成熟的函數(shù),常常都是編程人員在面對多種可能的函數(shù)設計方案進行綜合分析之后有所取舍而編寫出來的。我們在面對一個編程問題時,常常能構思出多種函數(shù)設計方案,這時需要學會取舍,選出其中一種自己覺得最為合理的方案來編程實現(xiàn)之。82小結(5.2程序的函數(shù)分解)程序要進行合理的函數(shù)分解。(1)重復出現(xiàn)的相同/相似計算片段;(2)長計算過程中有邏輯獨立性的片段。函數(shù)定義(封裝)把函數(shù)內外隔成兩個世界。定義函數(shù)和使用函數(shù)時要按不同的視角來看待函數(shù)。把原有的程序片段改寫成函數(shù):把前面的代碼中得到的數(shù)據(jù)改為函數(shù)的形參;把計算結果作為函數(shù)的返回值。編寫一個函數(shù)之后,通常需要自行編寫一個main函數(shù),對該函數(shù)進行測試。測試數(shù)據(jù)可能是手工輸入,也可以是通過循環(huán)變量來提供。通常不在函數(shù)中向屏幕打印輸出。以便讓調用處進行后續(xù)計算或屏幕輸出。要根據(jù)調用時的需求來處理函數(shù)定義中的參數(shù)為值參數(shù)或引用參數(shù)。每個問題都可能有多種函數(shù)分解方案,要選擇最合適的方案進行處理。講解了“5.2程序的函數(shù)分解”之后,最好提前講解“5.7程序動態(tài)除錯方法(二)”,然后再講其它節(jié)。8384第5章函數(shù)與程序結構5.1函數(shù)的定義與調用5.2程序的函數(shù)分解
5.3循環(huán)與遞歸5.4外部變量與靜態(tài)局部變量5.5聲明與定義5.6預處理5.7程序動態(tài)除錯方法(二)855.3循環(huán)與遞歸程序中有循環(huán)就可能導致很長的計算。沒有循環(huán)結構也能描述這類計算。C/C++語言允許遞歸,可在函數(shù)內調用自身,程序常常更簡單清晰。865.3.1階乘和乘冪(循環(huán),遞歸)【例5-16】定義計算整數(shù)階乘的函數(shù):n!=1×2×…×(n-1)×n乘的次數(shù)依賴于n,定義時未知,每次用可能不同。類型特征可定為:
intfact(int)階乘值增長極快(數(shù)學),更合適的類型特征:
longlong
fact(int)87可以用循環(huán)定義:intfactloop(intn){ //循環(huán)方式求階乘 intfac=1; for(inti=1;i<=n;++i) fac*=i; returnfac;}程序的典型情況:計算次數(shù)依賴于某些參數(shù)的值。88省略號不科學。嚴格定義需用遞歸形式。遞歸定義的形式也提出了一種計算方法:如果語言允許遞歸定義函數(shù),就可直接翻譯為程序。遞歸定義:在函數(shù)定義內部調用被定義函數(shù)本身。階乘函數(shù)的遞歸寫法:intfact(intn){//遞歸方式求階乘 returnn==0?1:n*fact(n-1);}比循環(huán)函數(shù)簡潔得多。階乘:n!=1×2×…×(n-1)×n89考慮負參數(shù)值處理??筛臑椋簄<=1?1:..調用fact(3)fact(3)3*fact(2)fact(2)2*fact(1)fact(1)1*fact(0)fact(0)返回值6返回值2返回值1返回值1fact(3)的計算過程遞歸定義導致的計算過程fact實現(xiàn)的計算過程很不簡單。計算中fact被遞歸調用的次數(shù)由實參確定。參數(shù)不同,則遞歸調用次數(shù)(步數(shù))不同。90【例5-17】遞歸求冪。寫函數(shù)doubledexp(intn)
求e(自然對數(shù)的底)的n次冪。注意到參數(shù)n為負時乘冪也有定義。先寫一個輔助函數(shù),再寫一個所需的函數(shù)。doubledexp1(intn){ //只處理n>=0 returnn==0?1:2.71828*dexp1(n-1);}doubledexp(intn){ //分為n>=0和n<0處理
returnn>=0?dexp1(n):1/dexp1(-n);}這個問題也可以用循環(huán)寫出(略)計算乘冪的函數(shù)也可以用循環(huán)的方式寫出:doubledexploop(intn){//循環(huán)方式求e的n次冪
doublex=2.71828183,d=1;
if(n<0){
n=-n;
x=1/x;
}
for(inti=0;i<n,++i)
d*=x;
returnd;}循環(huán)與遞歸的關系:有些循環(huán)程序也可以用遞歸的形式寫;有些遞歸程序也可以通過循環(huán)寫出。當然,遞歸算法必須定義為函數(shù)
(不能在main函數(shù)中寫遞歸)。9192從上面兩個例子可見,遞歸的函數(shù)定義需要使用條件表達式或條件語句。遞歸函數(shù)必須區(qū)分兩種情況:(1)直接給出結果的情況。是遞歸的基礎(2)需要遞歸處理的情況。其中把對較復雜情況的計算歸結為對更簡單情況的計算。intfact(intn){returnn==0?1:n*fact(n-1);}doubledexp1(intn){ //只處理n>=0returnn==0?1:2.71828*dexp1(n-1);}93【5-18】Fibonacci(斐波那契)序列的遞歸定義:遞歸函數(shù)定義:intfib(intn){returnn<=2?1:fib(n-1)+fib(n-2);}負參數(shù)值也定義為1。這是“合理”處置。問題分析:這個程序好不好?一方面,很好!程序與數(shù)學定義的關系很清晰,正確性容易確認,定義易讀易理解。5.3.2Fibonacci序列(計算與時間)94有何缺點?寫程序計時并分析:intmain(){intt0,t1;cout<<"n\tfib(n)\ttime(s)"<<endl;for(intn=10;n<=45;++n){
t0=clock();//調用fib(n)之前的時刻
cout<<n<<"\t"<<fib(n)<<"\t";//!!!
t1=clock();
////調用fib(n)結束之后的時刻
cout<<(double)(t1-t0)/CLOCKS_PER_SEC <<endl;//時間差
}return0;}95nfib(n)time(s)……(略)271964180.001283178110.002295142290.003308320400.0043113462690.0073221783090.013335245780.0163457028870.0253592274650.043614930
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 雞眼病因介紹
- 債務如何轉讓協(xié)議書
- 關于就業(yè)協(xié)議
- 個人單位租車協(xié)議
- 1.2《風景談》【中職專用】高一語文(高教版2023基礎模塊上冊)
- (2024)年產噸鋰電池負極材料石墨化項目可行性研究報告寫作模板(一)
- 2022-2023學年天津一中高一(上)期末語文試卷
- 2023年天津市南開區(qū)高考語文一模試卷
- 解析:內蒙古通遼市科爾沁左翼中旗2024-2025學年七年級上學期期中語文試題(原卷版)-A4
- 2024(半成品預制菜篇)餐飲供應鏈指南
- 24春國家開放大學《計算機網絡應用》大作業(yè)1-4參考答案
- 銀企對接方案
- 反恐防范重點目標檔案 空白模板2023年
- 科學技術中的倫理問題
- 中國子宮內膜增生管理指南(2022)解讀
- 四年級上學期體育理論試卷(附答案)
- 預防物體打擊安全課件
- 2024年村支書年度述職報告(四篇合集)
- 弱視斜視知識講座
- 湖南省雅禮中學2023-2024學年高一上學期12月月考歷史試卷
- 2023醫(yī)院內部審計工作計劃范文
評論
0/150
提交評論