defer

用於延遲函式的呼叫,每次defer都會把一個函式壓入棧中,函式返回前再把延遲的函式取出並執行

資料結構

type _defer struct {
sp uintptr //函式棧指標
pc uintptr //程式計數器
fn *funcval //函式地址
link *_defer //指向自身結構的指標,用於連結多個defer
}

規則一:延遲函式的引數在defer語句出現時就已經確定下來了

如:

func a() {
i := 0
defer fmt.Println(i)
i++
return
}

defer語句中的fmt.Println()引數i值在defer出現時就已經確定下來,實際上是拷貝了一份。後面對變數i的修改不會影響fmt.Println()函式的執行,仍然列印”0”。但是注意 這裡對於指標型別引數,仍然適用,所以defer後面的語句對變數修改還是會影響延遲函式如:

func a() {
i := []int{1,2,3}
defer fmt.Println(i)
i[0]=10
return
}//會輸出[10,2,3],因為這裡i為指標,是陣列的地址

規則二:先進後出

很好理解,延遲函式執行按先進後出順序執行,即先進去的defer最後執行

規則三:延遲函式可能操作主函式的具名返回值

defer可能會改變返回值,因為return語句不是原子的,實際執行為設定返回值-->ret

defer語句實際執行在返回前,即擁有defer的函式返回過程是:設定返回值–>執行defer–>ret。

注意:這裡返回值必須要有具體名字

如下:

func deferFuncReturn() (result int) {
i := 1 defer func() {
result++
}() return i
}

輸出2,return語句先把result設定為i的值,即1,defer語句又把result遞增1,所以最終返回2

如果返回的是匿名變數,則不改變,如:

func foo() int {
var i int i=1
defer func() {
i++
}() return i
}

這裡還是返回1,上面的返回語句可以拆分成以下過程:

t=i	//t就是要返回的值
i++
return //返回t,這裡t還是1