Go 中 defer 和 return 執行的先后順序
-
多個defer的執行順序為“后進先出”;
-
defer、return、返回值三者的執行邏輯應該是:return最先執行,return負責將結果寫入返回值中;接着defer開始執行一些收尾工作;最后函數攜帶當前返回值退出。
如果函數的返回值是無名的(不帶命名返回值),則go語言會在執行return的時候會執行一個類似創建一個臨時變量作為保存return值的動作,而有名返回值的函數,由於返回值在函數定義的時候已經將該變量進行定義,在執行return的時候會先執行返回值保存操作,而后續的defer函數會改變這個返回值(雖然defer是在return之后執行的,但是由於使用的函數定義的變量,所以執行defer操作后對該變量的修改會影響到return的值
eg1:不帶命名返回值的函數
package main import "fmt" func main() { fmt.Println("return:", test())// defer 和 return之間的順序是先返回值, i=0,后defer } func test() int {//這里返回值沒有命名 var i int defer func() { i++ fmt.Println("defer1", i) //作為閉包引用的話,則會在defer函數執行時根據整個上下文確定當前的值。i=2 }() defer func() { i++ fmt.Println("defer2", i) //作為閉包引用的話,則會在defer函數執行時根據整個上下文確定當前的值。i=1 }() return i }
test() 先返回 i=0
defer2先於defer1執行
輸出結果為:
defer2 1
defer1 2
return: 0
eg2:帶命名返回值的函數:
package main import "fmt" func main() { fmt.Println("return:", test()) } func test() (i int) { //返回值命名i defer func() { i++ fmt.Println("defer1", i) }() defer func() { i++ fmt.Println("defer2", i) }() return i }
對外部變量的引用
作為函數參數(i),則在defer申明時就把值傳遞給defer,
輸出結果為:
defer2 1
defer1 2
return: 2
理解return 返回值的運行機制:
為了弄清上述兩種情況的區別,我們首先要理解return 返回值的運行機制:
return 並非原子操作,分為賦值,和返回值兩步操作
eg1 : 實際上return 執行了兩步操作,因為返回值沒有命名,所以
return 默認指定了一個返回值(假設為s),首先將i賦值給s,后續
的操作因為是針對i,進行的,所以不會影響s, 此后因為s不會更新,所以
return s 不會改變
相當於:
var i int
s := i
return s
eg2 : 同上,s 就相當於 命名的變量i, 因為所有的操作都是基於
命名變量i(s),返回值也是i, 所以每一次defer操作,都會更新
返回值i