golang中的defer關鍵字指明,其後面的函數將在defer所在作用域的函數返回後,自動調用。只能是函數,不能是其他形式的表達式,如果要執行表達式,可以加一個匿名函數,且無返回值。形如:
func Foo(){
fmt.Println("defer call...")
}
func DeferCall(){
defer Foo()
defer func(){
fmt.Println("anonymous defer call...")
}()
}
當函數中有多個defer時,註冊的函數調用的順序: 先註冊後調用,後註冊先調用。跟入棧出棧的順序一樣。
有如下面試題:
package main
import "fmt"
func cacl(s string, a, b int) int {
ret := a + b
fmt.Println(s, a, b, ret)
return ret
}
func main() {
a := 1
b := 2
defer cacl("10", a, cacl("11", a, b)) // defer1
a = 0
defer cacl("20", a, cacl("22", a, b)) // defer2
b = 1
}
執行結果:
11 1 2 3
22 0 2 2
20 0 2 2
10 1 3 4
解釋:
在defer1時,註冊的是前面一個cacl,後面一個cacl並不是註冊到defer的。因此在進入main函數執行到defer時,會先調用後一個cacl,獲得返回值後,將此值作爲參數,傳遞給前一個cacl。此時參數1和參數2也拷貝一份並保存在函數棧中。defer2也是如此。在main函數返回後,根據先註冊後調用,後註冊先調用的規則,將先執行cacl("20", 0, 2)
, 再執行cacl("10", 1, 3)
。注意:a,b的值都是值傳遞,因此,再a,b的值更改後,並不影響已經註冊cacl的參數的值。