原文連接:https://studygolang.com/articles/4809golang
Go語言中延遲函數defer充當着 cry...catch 的重任,使用起來也很是簡便,然而在實際應用中,不少gopher並無真正搞明白defer、return和返回值之間的執行順序,從而掉進坑中,今天咱們就來揭開它的神祕面紗!函數
先來運行下面兩段代碼:指針
A. 無名返回值的狀況內存
package main import ( "fmt" ) func main() { fmt.Println("return:", a()) // 打印結果爲 return: 0 } func a() int { var i int defer func() { i++ fmt.Println("defer2:", i) // 打印結果爲 defer: 2 }() defer func() { i++ fmt.Println("defer1:", i) // 打印結果爲 defer: 1 }() return i }
B. 有名返回值的狀況class
package main import ( "fmt" ) func main() { fmt.Println("return:", b()) // 打印結果爲 return: 2 } func b() (i int) { defer func() { i++ fmt.Println("defer2:", i) // 打印結果爲 defer: 2 }() defer func() { i++ fmt.Println("defer1:", i) // 打印結果爲 defer: 1 }() return i // 或者直接 return 效果相同 }
先來假設出結論,幫助你們理解緣由:import
多個defer的執行順序爲「後進先出」;變量
defer、return、返回值三者的執行邏輯應該是:return最早執行,return負責將結果寫入返回值中;接着defer開始執行一些收尾工做;最後函數攜帶當前返回值退出。im
如何解釋兩種結果的不一樣:語言
上面兩段代碼的返回結果之因此不一樣,其實從上面第2條結論很好理解。co
a()int 函數的返回值沒有被提早聲名,其值來自於其餘變量的賦值,而defer中修改的也是其餘變量,而非返回值自己,所以函數退出時返回值並無被改變。
b()(i int) 函數的返回值被提早聲名,也就意味着defer中是能夠調用到真實返回值的,所以defer在return賦值返回值 i 以後,再一次地修改了 i 的值,最終函數退出後的返回值纔會是defer修改過的值。
C. 下面咱們再來看第三個例子,驗證上面的結論:
package main import ( "fmt" ) func main() { fmt.Println("c return:", *(c())) // 打印結果爲 c return: 2 } func c() *int { var i int defer func() { i++ fmt.Println("c defer2:", i) // 打印結果爲 c defer: 2 }() defer func() { i++ fmt.Println("c defer1:", i) // 打印結果爲 c defer: 1 }() return &i }
雖然 c()*int 的返回值沒有被提早聲明,可是因爲 c()*int 的返回值是指針變量,那麼在return將變量 i 的地址賦給返回值後,defer再次修改了 i 在內存中的實際值,所以函數退出時返回值雖然依舊是原來的指針地址,可是其指向的內存實際值已經被成功修改了。
即,咱們假設的結論是正確的!