日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Go語言defer與return執行的先后順序詳解_Golang

作者:捶捶自己 ? 更新時間: 2023-01-10 編程語言

先了解什么是defer

Go語言中的defer與return執行的先后順序

Go語言的 defer 語句會將其后面跟隨的語句進行延遲處理,在 defer 歸屬的函數即將返回時,將延遲處理的語句按 defer 的逆序進行執行.也就是說,先被 defer 的語句最后被執行,最后被 defer 的語句,最先被執行。(與棧的先入后出是一個道理,也可以將其理解為入棧和出棧)

舉一個簡單的例子

func main() {
   a, b := 111, 333
   defer fmt.Println("b= ", b)
   fmt.Println("a= ", a)
}
打印結果:
a=  111
b=  333

可以看到雖然執行語句時b在前,但是輸出結果為b在最后被輸出。

defer 的用法

(簡單講解,細節請自行查閱資料)

一般用來釋放資源或者讀寫操作,當處理業務或邏輯中涉及成對的操作是一件比較煩瑣的事情,比如打開和關閉文件、接收請求和回復請求、加鎖和解鎖等。在這些操作中,最容易忽略的就是在每個函數退出處正確地釋放和關閉資源。比如下面一個例子

func main(){
    a := 1
    out := bufio.NewWriter(os.Stdout)
    defer out.Flush()
    fmt.Fprintln(out, a)
}
輸出結果:
1

就可以在最后將結果打印到控制臺中去,類似的用法如關閉數據庫資源等等。如果這個例子太過于簡單,那么來看下個例子。

var a bool = true
defer func() {
   fmt.Println("1")
}()
if a == true {
   fmt.Println("2")
   return
}
defer func() {
   fmt.Println("3")
}()
輸出結果:
2
1

我們會發現defer語句也是需要被執行的,如果在defer函數執行之前就執行return。defer后的語句就不會再被執行了。但是如果是在return之前defer已經執行,則defer中的語句將會在return執行之前先一步進行執行.

那么defer 和 return有什么聯系?

defer 是延遲執行語句,return是返回語句,那么肯定出現誰先誰后的問題。下面看一個經典的例子吧

func increaseA() int {
	var i int
	defer func() {
		i++
	}()
	return i
}
func increaseB() (r int) {
	defer func() {
		r++
	}()
	return r
}
func main() {
	fmt.Println(increaseA())
	fmt.Println(increaseB())
}
輸出結果為:
0
1

肯定有人覺得有疑惑,為什么A函數沒有輸出,B函數卻輸出了呢?為什么答案不是1和0呢?

原因:

先說結論:defer 修飾的匿名函數,只能更新具名返回值.那么這會導致什么問題呢?我們來逐步分析這個例子。

  • 在increaseA()函數中有一個聲明i,表示i在該函數內已經被生成,是有名稱的變量。但該函數返回參數為匿名參數.
  • 在increaseB()函數中沒有聲明r,是匿名變量。但該函數返回參數為具名參數.
  • func increaseA() int,返回值i=0的時候該值已經被綁定到返回值里了,defer再去改i已經沒用了.
  • func increaseB() (r int), 返回值r先把返回變量設為0,defer又把r改為1.這時候還能生效. 因此答案很明顯為 1 和 0.

更進一步理解

我們若想要進一步理解也可以去輸出匯編語句,然后進行研讀,可惜我是個菜鳥讀不懂匯編語言!但我們可以從return入手

我們要理解return 返回值的運行機制:

return并非原子操作,分為賦值,和返回值兩步操作.實際上return 執行了兩步操作,因為返回值沒有命名,所以return默認指定了一個返回值(假設為a),首先將i賦值給a,后續的操作因為是針對i進行的,所以不會影響a, 此后因為a不會更新,所以return a不會改變.

var i int  
a := i  
return a

但是如果return的參數a是具名參數,就像上述例子中increaseB()函數一樣。a就相當于命名的變量i, 因為所有的操作都是基于命名變量i(a),返回值也是i, 所以每一次defer操作,都會更新返回值i.

省流小結

return會將返回值先保存起來,對于無名返回值來說,保存在一個臨時對象中,defer是看不到這個臨時對象的;而對于有名返回值來說,就保存在已命名的變量中。

原文鏈接:https://juejin.cn/post/7171066100052918308

欄目分類
最近更新