對於遞迴函式,大家都很熟悉了,對於解析一些複雜資料結構方面,能夠使**非常簡潔,明了。
從我的理解來說,遞迴函式一般效率比較低,而且自身特點導致的限制也不少:
1. 效率低
乙個非常簡單的例子數數吧,從1數到1m。
對於迴圈實現的:
遞迴實現:
對於迴圈實現,函式相當於:
對於遞迴實現,相當於:
對於第一種實現,不需要函式呼叫,只需要重複1m-1次跳轉,即loop
對於遞迴實現,需要1m-1次函式呼叫
讓我們來看看跳轉指令和函式呼叫指令的區別:
對於跳轉指令,相當於將目標指令位址附給eip暫存器,該條指令結束後計算機會從eip指向的指令位址開始執行。
即: jmp ***x ;此指令的工作是僅僅是修改 eip的值為***x對應的位址, 當該指令結束後系統會從eip指向處開始執行
對於函式呼叫指令call,則作了更多隱藏工作:
call ***x ;因為call指令是需要返回的,所以需要儲存返回後的位址,該位址為執行call指令時的eip值+5(call指令的長度為5,32位系統)
;然後將這個值儲存到堆疊上,之後才能將***x對應的值裝載到eip暫存器之中,開始實際跳轉動作
;自過程結束後,再將儲存在堆疊上的值重新附給eip,這樣程式才能從call ***x的下一條指令開始執行。
所以call ***x 基本相當於
push eip+5
mov eip, ***x
jmp eip
ret 相當於:
pop eip ;即將前面的eip+5重新附給eip
jmp eip
從這裡基本可以看出:遞迴函式實際上多做了(1m-1)次多餘的工作,而且這只是最簡單的寒暑,如果自過程中還有很多臨時變數,則整個過程中
維護堆疊平衡的開銷也將非常可觀。
2. 侷限性
不知道這個算不算,因為世紀中還沒真正遇到過這個問題導致的執行錯誤。
對於乙個最簡單的遞迴函式:
這個函式執行的結果是導致執行棧耗盡,最終程式崩潰,當然最終原因是它缺少適當的遞迴出口。
不過對於一種極端情況:
這個函式每次都會在棧上申請 10000 或20000個byte, 取決於字符集屬性,由於執行棧大小是預先定義好的(2m左右吧),所以該函式也不可避免的導致上面的問題。
綜上,對於對效率要求很高的**應盡量避免使用遞迴;
在使用遞迴時,如果遞迴深度不能預期, 避免在堆疊上申請只有函式返回才能釋放的大塊空間,而應該將其放在heap上。
以上為個人理解,估計肯定有先人總結過了,但絕非抄襲,同時歡迎拍磚。
遞迴函式的一點小感悟
最近在複習python的遞迴函式部分,突然有了一些小感悟,想趕緊先記錄下來,日後再深入思考,完善這篇文章 之前寫遞迴函式的題目總是無從下手。後來我突然聯想到高中數學 好像離散數學中也說過 有乙個關於遞迴證明法的介紹,對於乙個命題的證明,首先我們得證明某一種特殊情況的成立,其次對於任意一般情況,我們都...
小K對鍊錶的一點理解
馬上就要踏上社會,回想有點羞愧大學這幾年也沒留下什麼。最近需要面試一家公司,所以需要把以前的一些東西複習一下,所以順便就寫乙個部落格吧。這次是寫的對鍊錶的複習。剛回想鍊錶的時候就知道有個頭指標,有乙個當前位置的指標,有乙個專門儲存資料的指標。然後乙個存乙個記錄不斷交替進行就行。其實也就是這樣的。只是...
對 threadfence的一點理解
一直沒搞清楚,cuda 2.2版增加的 threadfence到底有何作用,直到今天看到sdk 3.0手冊 中的下面例子才恍然大悟.中文為我的理解,嘿嘿 乙個求和的例子 device unsigned int count 0 統計有幾個block結束的變數 shared bool islastblo...