鑽牛角尖之try return finally

2022-03-31 13:27:44 字數 3155 閱讀 3866

try catch finally是我們最常用的異常處理的流程,我們都知道執行try塊**,如果有異常發生就會被相應catch捕獲到執行catch塊**,無論如何finally塊的**都會被執行。但是如果我們在try塊中加入return語句,return和finally的執行順序呢?

對此做過試驗或者從finally總會被執行的作用來說,都會認為finally在return前執行。不過,看下面的例子。

js**:

function testtry()  catch (e)  finally 

}

.net**:

private int32 testtry()

catch

finally

}

結果應該是1還是3呢?如果finally在return之前那應該是3啊,但是上面兩段**是執行是乙個結果:1。

難道函式或方法遇到return直接返回,finally根本就沒有執行??這不是和finally總會被執行的作用矛盾嗎?

看這段**:

function testtry()  catch (e)  finally 

}

因為.net不允許在finally中加return,因此沒有了.net版本的這段**。

這段js**比之前的只是在return中多了乙個return,結果應該是什麼?1 or 3?

答案是3,這又能說明什麼?它說明不管return和finally的執行順序是怎樣,finally肯定是被執行了。

那問題又來了,既然finally肯定被執行了,那我們的第一段**結果就應該是3,而不應該是1啊?

如何揭秘,我們就要借助第一段**中.net**的編譯**:

18:         

25: catch

00000063 90 nop

26:

30: finally

31:

00000094 90 nop

00000095 58 pop eax

00000096 ff e0 jmp eax

00000098 90 nop

34: }

00000099 8b 45 bc mov eax,dword ptr [ebp-44h]

0000009c 8d 65 f4 lea esp,[ebp-0ch]

0000009f 5b pop ebx

000000a0 5e pop esi

000000a1 5f pop edi

000000a2 5d pop ebp

000000a3 c3 ret

000000a4 c7 45 e4 00 00 00 00 mov dword ptr [ebp-1ch],0

000000ab eb eb jmp 00000098

000000ad c7 45 e4 00 00 00 00 mov dword ptr [ebp-1ch],0

000000b4 eb e2 jmp 00000098

這其實是.net執行的真實路徑。

1,首先第乙個我們可以看到的是 倒數第5行的ret指令,這個是返回指令,也就是說我們表面的return其實並不是真實的方法出口的位置。

2,看下return i;的il

00000047 8b 45 c0             mov         eax,dword ptr [ebp-40h] 

0000004a 89 45 bc mov dword ptr [ebp-44h],eax

0000004d 90 nop

0000004e c7 45 e0 00 00 00 00 mov dword ptr [ebp-20h],0

00000055 c7 45 e4 fc 00 00 00 mov dword ptr [ebp-1ch],0fch

0000005c 68 8d 18 e5 03 push 3e5188dh

00000061 eb 29 jmp 0000008c

我們看到除了一些mov操作之外,並沒有中止方法執行,最後一句是jmp跳轉的指令,而這個跳轉的位址正好是finally塊的開始的位址,也就是這句執行之後去執行了finally。

4,接著上一塊來說,我們可以看到每次操作i值時,都會有mov dword ptr [ebp-40h] 這樣的操作,也就是說這塊位址儲存的是i的值。那麼我們從return i;的il**可以看到它首先執行了兩個操作:把[ebp-40h]的值給eax,然後又把eax值給了[ebp-44h],也就是其實返回值儲存在了[ebp-44h]這個位址。

5,到最後,只是把[ebp-44h]這個位址的值放到資料暫存器,最後被呼叫者獲取。

從此真相大白,也就是變數和返回值是分別儲存在兩個不同的地方,return i;時只用i值填充返回值的位址,finally時再次改變i的值,卻不會影響返回值。至於js finally裡能再次return i;也可能是再次修改了返回值那塊位址所儲存的值。

對於值型別,分配的位址儲存直接是值,再次修改i值不能影響到返回值;而對於引用型別,位址裡儲存的是指標,是不是應該是另一番光景呢?

function testtry2() ;

o.i = 0;

try catch (e) finally

}

大膽猜一次返回的物件的i的值吧,對,就是3.

鑽完收工。

鑽牛角尖的成因和如何克服

為什麼會鑽牛角尖的5大因素 1 心理消極。想不開的人往往表現為性格內向,情緒消沉,自信心不足,喜歡以消極的態度看待事物,對事物的結局估計悲觀。2 偏執人格。想不開的人往往把思維停留在某一點上,不輕易改變自己的態度和認識,並沿著偏執認識走下去,甚至碰得頭破血流也不回頭。3 固定思維。想不開的人思考問題...

遞迴思想(鑽出牛角尖)

遞迴思想 函式中呼叫函式並使用返回值 經典案例 快速指數 目的 求值的整數次冪的聰明演算法 規格說明 利用遞迴的方法減少運算計算出整數次冪 整數冪的運算法則 a 4 a x a x a x a 也可以寫成a 4 a 2 x a 2 如果是奇數冪的 a 5 a 2 x a 2 x a 我們可以利用這種...

php的要注意的一些小細節和牛角尖

轉義 單引號只轉義 和 其他的不轉義 變數解析 雙引號裡面變數是可以解析的 速度 單引號不需要解析串內有沒有變數,需要轉義的內容也少,速度快點 heredoc 類似雙引號 和newdoc 類似單引號 的區別類似 如果不宣告鍵,會從0,1,2,遞增來生成鍵 array a b c 如果已存在某乙個或者...