程序員大神教你學C語言編程——遞歸函數(shù)

一、什么是遞歸函數(shù)

(1)遞歸函數(shù)即自調用函數(shù),在函數(shù)內部直接或間接地自己調用自己,即函數(shù)的嵌套調用是函數(shù)本身。

從字面上來看遞歸,遞即遞推,采用循環(huán)的思路來描述復雜問題的方法。在遞推階段每一個遞歸調用通過進一步調用自己來記住這次遞歸過程,當其中調用滿足終止條件時,遞推結束。歸即回歸,函數(shù)調用已逆序的方式回歸,知道最初調用的函數(shù)返回為止,此時遞歸過程結束。舉個例子:

(2)遞歸的基本原理

第一:每一級的函數(shù)調用都有自己的變量。

第二:每一次函數(shù)調用都會有一次返回。

第三:遞歸函數(shù)中,位于遞歸調用前的語句和各級被調用函數(shù)具有相同的執(zhí)行順序。

第四:遞歸函數(shù)中,位于遞歸調用后的語句的執(zhí)行順序和各個被調用函數(shù)的順序相反。

第五:雖然每一級遞歸都有自己的變量,但是函數(shù)代碼并不會得到復制。

最后:遞歸函數(shù)中必須包含可以終止遞歸調用的語句。

(3)遞歸的優(yōu)缺點

其優(yōu)點在于為某些變成問題提供了最簡單的解決方法,簡化了程序設計。而缺點是一些遞歸算法會很快耗盡計算機的內存資源。同時,使用遞歸的程序難于閱讀和維護。

二、函數(shù)調用機制的說明

任何函數(shù)之間不能嵌套定義,調用函數(shù)與被調用函數(shù)之間相互獨立遞歸函數(shù)的概念用法與實例(彼此可以調用)。發(fā)生函數(shù)調用時,被調用函數(shù)中保護了調用函數(shù)的運行環(huán)境和返回地址,使得調用函數(shù)的狀態(tài)可以被調用函數(shù)運行返回完全恢復,而且該狀態(tài)與被調用函數(shù)無關。

被調用函數(shù)運行的代碼雖然是同一個函數(shù)的代碼體,但由于調用點,調用時狀態(tài),返回點的不同,可以看作是函數(shù)的一個副本,與調用函數(shù)的代碼無關,所以函數(shù)的代碼是獨立的。被調用函數(shù)運行的棧空間獨立于調用函數(shù)的棧空間,所以與調用函數(shù)之間的數(shù)據(jù)也是無關的。函數(shù)之間考參數(shù)傳遞和返回值來聯(lián)系,函數(shù)看作為黑盒。

這種機制決定了 C/C++ 允許函數(shù)遞歸調用。

上面這段話的意思可以理解為,遞歸函數(shù)調用自身函數(shù)的時候,可以看作調用的是調用別的函數(shù)。

再有,被調用函數(shù)運行的棧空間獨立于調用函數(shù)的棧空間 這句話如何理解?

首先,我們先說一下棧的概念:

參看:遞歸函數(shù)的概念用法與實例

參看:C語言再學習 -- 內存管理

棧是一個后進先出(LIFO)的壓入(push)和彈出(pop)式的數(shù)據(jù)結構。在程序運行時,系統(tǒng)每次向棧中壓入一個對象,然后棧指針向下移動一個位置。當系統(tǒng)從棧中彈出一個對象時,最近進棧的對象將被彈出。然后棧指針向上移動一個位置。程序員經常利用棧這種數(shù)據(jù)結構來處理那些最適合用后進先出邏輯來描述的編程問題。這里討論的程序中的棧每個程序中都是存在的,它不需要程序員編寫代碼去維護,而是由運行時系統(tǒng)自動處理。所謂系統(tǒng)自動維護,實際上就是編譯器所產生的程序代碼。盡管在程序中看不到他們,但是程序員應該對此有所了解。

再看看程序中的棧是如何工作的。當一個函數(shù)(調用者)調用另一個函數(shù)(被調用者)時,運行系統(tǒng)將把調用者的所有實參和返回地址壓入棧中,棧指針將移到合適的位置來容納這些數(shù)據(jù)。最后進棧的是調用者的返回地址。當被調用這開始執(zhí)行時,系統(tǒng)把被調用者的自變量壓入棧中,并把棧指針再向下移,以保證有足夠的空間存儲被調用這聲明的所有自變量。當調用這把實參壓入棧后,被調用者就在棧中以自變量的形式建立形參。被調用這內部的其他自變量也是存放在棧中的。由于這些進棧操作,棧指針已經移動所有這些局部變量之下。但是被調用者記錄了它剛開始執(zhí)行時的初始棧指針,以它為參考,用正或負的偏移量來訪問棧中的變量。當被調用者準備返回時,系統(tǒng)彈出棧中所有的自變量,這是棧指針移動了被調用者剛開始執(zhí)行時的位置。接著被調用者返回,系統(tǒng)從棧中彈出返回地址,調用者就可以繼續(xù)執(zhí)行了。當調用者繼續(xù)執(zhí)行時,系統(tǒng)還將從棧中彈出調用者的實參,于是棧指針回到了調用發(fā)生前的位置。

現(xiàn)在接著說遞歸:

上面有提到了函數(shù)調用機制。遞歸之所以能實現(xiàn),是因為函數(shù)的每個執(zhí)行過程都在棧中有自己的形參和局部變量的拷貝,這些拷貝和函數(shù)的其他執(zhí)行過程毫不相干。這種機制是當代大多數(shù)程序設計語言實現(xiàn)子程序結構的基礎,是使得遞歸成為可能。假定某個調用函數(shù)調用了一個被調用函數(shù),再假定被調用函數(shù)又反過來調用了調用函數(shù)。這第二個調用就被稱為調用函數(shù)的遞歸,因為它發(fā)生在調用函數(shù)的當前執(zhí)行過程運行完畢之前。而且,因為這個原先的調用函數(shù)、現(xiàn)在的被調用函數(shù)在棧中較低的位置有它獨立的一組參數(shù)和自變量,原先的參數(shù)和變量將不受影響,所以遞歸能正常工作。程序遍歷執(zhí)行這些函數(shù)的過程就被稱為遞歸下降。

程序員需保證遞歸函數(shù)不會隨意改變靜態(tài)變量和全局變量的值,以避免在遞歸下降過程中的上層函數(shù)出錯。程序員還必須確保有一個終止條件來結束遞歸下降過程,并且返回到頂層。看下面的例子:

這里有一個問題一定要注意,就是static int sum = 0;

有些人就不明白,為什么要使用 static 類型修飾符,為什么不使用 int sum=0;?如果使用 int sum=0; 這樣的語句,在每次調用函數(shù)add()的時候,sum的值都是賦值為0,也就是第一步雖然加了1上來,可是第二次調用的時候,sum又回到了0。我們前面說了,static能保證本次初始化的值是上次執(zhí)行后的值,這樣也就保證了前面想加的結果不會丟失。如果你修改為int sum=0,最后結果一定是最后結果是5而不是15。

上面的例子就很好的解釋了,被調用函數(shù)運行的棧空間獨立于調用函數(shù)的棧空間 這句話。

三、遞歸調用的形式

遞歸函數(shù)有直接遞歸調用和間接調用兩種形式.

直接遞歸即在函數(shù)出現(xiàn)調用函數(shù)本身,例如:

間接遞歸調用指函數(shù)中調用其他函數(shù),而該其他函數(shù)卻又調用了本函數(shù)。例如:

下面,再擴展一種遞歸,尾遞歸

當遞歸調用是整個函數(shù)中最后執(zhí)行的語句且它的返回值不屬于表達式的一部分時,這個遞歸調用就是尾遞歸的。

上面的直接遞歸例子的返回值是 return num*func(num-1); 是一個表達式自然不是尾遞歸了,將其改為尾遞歸:

當編譯器檢查到一個函數(shù)是尾遞歸的時候,它就會覆蓋當前活躍記錄而不是在棧中去創(chuàng)建一個新的,這樣就解決了普通遞歸函數(shù)占用棧空間過大的問題。遺憾的是,大多數(shù)編程語言沒有針對尾遞歸做優(yōu)化,所以,即使把函數(shù)改成尾遞歸方式,也會導致棧溢出。星辰就暫時把遞歸說到這,下一篇的話繼續(xù)接上,內容還是講遞歸吧,基于太多,一下子寫太多的話,各位看了也會累。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,923評論 6 535
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,740評論 3 420
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,856評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,175評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,931評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,321評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,383評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,533評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 49,082評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,891評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,618評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,319評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,732評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,987評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,794評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,076評論 2 375

推薦閱讀更多精彩內容

  • 原文地址:C語言函數(shù)調用棧(一)C語言函數(shù)調用棧(二) 0 引言 程序的執(zhí)行過程可看作連續(xù)的函數(shù)調用。當一個函數(shù)執(zhí)...
    小豬啊嗚閱讀 4,655評論 1 19
  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,870評論 0 38
  • mTabLayout就是tablayout對象。drawable自己隨便畫一個shape或者叫設計弄個圖。效果如下:
    jiongge閱讀 2,783評論 0 0
  • 我覺得,貓是一種至陰之物。就像西方總把體態(tài)純黑的貓視為厄運的象征。有很多的婦女被巫女殺死,原因就是她們都養(yǎng)了一只黑...
    奶味蘿莉閱讀 552評論 0 0
  • 假裝是一棵樹 一棵只會落葉 不會開花的樹 一棵可以 讓樹葉起舞奏樂的樹 一棵喜歡聽故事 每年只聽一個 能聽一萬年的...
    天野丟閱讀 313評論 0 2