Go語(yǔ)言defer與return執(zhí)行的先后順序詳解
先了解什么是defer
Go語(yǔ)言中的defer與return執(zhí)行的先后順序
Go語(yǔ)言的 defer 語(yǔ)句會(huì)將其后面跟隨的語(yǔ)句進(jìn)行延遲處理,在 defer 歸屬的函數(shù)即將返回時(shí),將延遲處理的語(yǔ)句按 defer 的逆序進(jìn)行執(zhí)行.也就是說,先被 defer 的語(yǔ)句最后被執(zhí)行,最后被 defer 的語(yǔ)句,最先被執(zhí)行。(與棧的先入后出是一個(gè)道理,也可以將其理解為入棧和出棧)
舉一個(gè)簡(jiǎn)單的例子
func main() { a, b := 111, 333 defer fmt.Println("b= ", b) fmt.Println("a= ", a) } 打印結(jié)果: a= 111 b= 333
可以看到雖然執(zhí)行語(yǔ)句時(shí)b在前,但是輸出結(jié)果為b在最后被輸出。
defer 的用法
(簡(jiǎn)單講解,細(xì)節(jié)請(qǐng)自行查閱資料)
一般用來(lái)釋放資源或者讀寫操作,當(dāng)處理業(yè)務(wù)或邏輯中涉及成對(duì)的操作是一件比較煩瑣的事情,比如打開和關(guān)閉文件、接收請(qǐng)求和回復(fù)請(qǐng)求、加鎖和解鎖等。在這些操作中,最容易忽略的就是在每個(gè)函數(shù)退出處正確地釋放和關(guān)閉資源。比如下面一個(gè)例子
func main(){ a := 1 out := bufio.NewWriter(os.Stdout) defer out.Flush() fmt.Fprintln(out, a) } 輸出結(jié)果: 1
就可以在最后將結(jié)果打印到控制臺(tái)中去,類似的用法如關(guān)閉數(shù)據(jù)庫(kù)資源等等。如果這個(gè)例子太過于簡(jiǎn)單,那么來(lái)看下個(gè)例子。
var a bool = true defer func() { fmt.Println("1") }() if a == true { fmt.Println("2") return } defer func() { fmt.Println("3") }() 輸出結(jié)果: 2 1
我們會(huì)發(fā)現(xiàn)defer語(yǔ)句也是需要被執(zhí)行的,如果在defer函數(shù)執(zhí)行之前就執(zhí)行return。defer后的語(yǔ)句就不會(huì)再被執(zhí)行了。但是如果是在return之前defer已經(jīng)執(zhí)行,則defer中的語(yǔ)句將會(huì)在return執(zhí)行之前先一步進(jìn)行執(zhí)行.
那么defer 和 return有什么聯(lián)系?
defer 是延遲執(zhí)行語(yǔ)句,return是返回語(yǔ)句,那么肯定出現(xiàn)誰(shuí)先誰(shuí)后的問題。下面看一個(gè)經(jīng)典的例子吧
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()) } 輸出結(jié)果為: 0 1
肯定有人覺得有疑惑,為什么A函數(shù)沒有輸出,B函數(shù)卻輸出了呢?為什么答案不是1和0呢?
原因:
先說結(jié)論:defer 修飾的匿名函數(shù),只能更新具名返回值.那么這會(huì)導(dǎo)致什么問題呢?我們來(lái)逐步分析這個(gè)例子。
- 在increaseA()函數(shù)中有一個(gè)聲明i,表示i在該函數(shù)內(nèi)已經(jīng)被生成,是有名稱的變量。但該函數(shù)返回參數(shù)為匿名參數(shù).
- 在increaseB()函數(shù)中沒有聲明r,是匿名變量。但該函數(shù)返回參數(shù)為具名參數(shù).
- func increaseA() int,返回值i=0的時(shí)候該值已經(jīng)被綁定到返回值里了,defer再去改i已經(jīng)沒用了.
- func increaseB() (r int), 返回值r先把返回變量設(shè)為0,defer又把r改為1.這時(shí)候還能生效. 因此答案很明顯為 1 和 0.
更進(jìn)一步理解
我們?nèi)粝胍M(jìn)一步理解也可以去輸出匯編語(yǔ)句,然后進(jìn)行研讀,可惜我是個(gè)菜鳥讀不懂匯編語(yǔ)言!但我們可以從return入手
我們要理解return 返回值的運(yùn)行機(jī)制:
return
并非原子操作,分為賦值,和返回值兩步操作.實(shí)際上return
執(zhí)行了兩步操作,因?yàn)榉祷刂禌]有命名,所以return
默認(rèn)指定了一個(gè)返回值(假設(shè)為a),首先將i賦值給a,后續(xù)的操作因?yàn)槭轻槍?duì)i進(jìn)行的,所以不會(huì)影響a, 此后因?yàn)閍不會(huì)更新,所以return a
不會(huì)改變.
var i int a := i return a
但是如果return的參數(shù)a是具名參數(shù),就像上述例子中increaseB()函數(shù)一樣。a就相當(dāng)于命名的變量i, 因?yàn)樗械牟僮鞫际腔诿兞縤(a),返回值也是i, 所以每一次defer操作,都會(huì)更新返回值i.
省流小結(jié)
return會(huì)將返回值先保存起來(lái),對(duì)于無(wú)名返回值來(lái)說,保存在一個(gè)臨時(shí)對(duì)象中,defer是看不到這個(gè)臨時(shí)對(duì)象的;而對(duì)于有名返回值來(lái)說,就保存在已命名的變量中。
以上就是Go語(yǔ)言defer與return執(zhí)行的先后順序詳解的詳細(xì)內(nèi)容,更多關(guān)于Go defer return執(zhí)行順序的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
golang中連接mysql數(shù)據(jù)庫(kù)
這篇文章主要介紹了golang中連接mysql數(shù)據(jù)庫(kù)的步驟,幫助大家更好的理解和學(xué)習(xí)go語(yǔ)言,感興趣的朋友可以了解下2020-12-12go goroutine 怎樣進(jìn)行錯(cuò)誤處理
在 Go 語(yǔ)言程序開發(fā)中,goroutine 的使用是比較頻繁的,因此在日常編碼的時(shí)候 goroutine 里的錯(cuò)誤處理,怎么做會(huì)比較好呢,本文就來(lái)詳細(xì)介紹一下2021-07-07利用golang進(jìn)行OpenCV學(xué)習(xí)和開發(fā)的步驟
目前,OpenCV逐步成為一個(gè)通用的基礎(chǔ)研究和產(chǎn)品開發(fā)平臺(tái),下面這篇文章主要給大家介紹了關(guān)于利用golang進(jìn)行OpenCV學(xué)習(xí)和開發(fā)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2018-09-09Golang Gorm實(shí)現(xiàn)自定義多態(tài)模型關(guān)聯(lián)查詢
GORM 是一個(gè)流行的開源 ORM (Object-Relational Mapping) 庫(kù),專為 Go 語(yǔ)言設(shè)計(jì),它簡(jiǎn)化了與 SQL 數(shù)據(jù)庫(kù)的交互,GORM 封裝了數(shù)據(jù)庫(kù)操作,使得開發(fā)者能夠通過簡(jiǎn)單的鏈?zhǔn)秸{(diào)用來(lái)執(zhí)行 CRUD,本文給大家介紹了Golang Gorm實(shí)現(xiàn)自定義多態(tài)模型關(guān)聯(lián)查詢,需要的朋友可以參考下2024-11-11go語(yǔ)言K8S?的?informer機(jī)制淺析
這篇文章為大家主要介紹了go語(yǔ)言K8S?的?informer機(jī)制淺析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10golang gorm 計(jì)算字段和獲取sum()值的實(shí)現(xiàn)
這篇文章主要介紹了golang gorm 計(jì)算字段和獲取sum()值的實(shí)現(xiàn)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2020-12-12Golang中的信號(hào)(Signal)機(jī)制詳解
Signal 是一種操作系統(tǒng)級(jí)別的事件通知機(jī)制,進(jìn)程可以響應(yīng)特定的系統(tǒng)信號(hào),這些信號(hào)用于指示進(jìn)程執(zhí)行特定的操作,如程序終止、掛起、恢復(fù)等,Golang 的標(biāo)準(zhǔn)庫(kù) os/signal 提供了對(duì)信號(hào)處理的支持,本文將詳細(xì)講解 Golang 是如何處理和響應(yīng)系統(tǒng)信號(hào)的,需要的朋友可以參考下2024-01-01