Golang中defer、return、返回值之間執行順序的坑


原文鏈接:https://studygolang.com/articles/4809

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. 有名返回值的情況

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 效果相同
}

先來假設出結論,幫助大家理解原因:

  1. 多個defer的執行順序為“后進先出”;

  2. defer、return、返回值三者的執行邏輯應該是:return最先執行,return負責將結果寫入返回值中;接着defer開始執行一些收尾工作;最后函數攜帶當前返回值退出。

如何解釋兩種結果的不同:

上面兩段代碼的返回結果之所以不同,其實從上面第2條結論很好理解。

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 在內存中的實際值,因此函數退出時返回值雖然依舊是原來的指針地址,但是其指向的內存實際值已經被成功修改了。

即,我們假設的結論是正確的!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM