## 延遲函數可能操作主函數的具名返回值
定義defer的函數,即主函數可能有返回值,返回值有沒有名字沒有關系,defer所作用的函數,即延遲函數可能會影響到返回值
### 1 函數返回過程
有一個事實必須要了解,關鍵字*return*不是一個原子操作,實際上*return*只代理匯編指令*ret*,即將跳轉程序執行。比如語句`return i`,實際上分兩步進行,即將i值存入棧中作為返回值,然后執行跳轉,而defer的執行時機正是跳轉前,所以說defer執行時還是有機會操作返回值的。
舉個實際的例子進行說明這個過程:
~~~go
func deferFuncReturn() (result int) {
i := 1
defer func() {
result++
}()
return i
}
~~~
該函數的return語句可以拆分成下面兩行:
~~~go
result = i
return
~~~
而延遲函數的執行正是在return之前,即加入defer后的執行過程如下:
~~~go
result = i
result++
return
~~~
所以上面函數實際返回i++值。
### 2 主函數擁有匿名返回值,返回字面值
一個主函數擁有一個匿名的返回值,返回時使用字面值,比如返回”1”、”2”、”Hello”這樣的值,這種情況下defer語句是無法操作返回值的。
一個返回字面值的函數,如下所示:
~~~go
func foo() int {
var i int
defer func() {
i++
}()
return 1
}
~~~
上面的return語句,直接把1寫入棧中作為返回值,延遲函數無法操作該返回值,所以就無法影響返回值
### 3 主函數擁有匿名返回值,返回變量
一個主函數擁有一個匿名的返回值,返回使用本地或全局變量,這種情況下defer語句可以引用到返回值,但不會改變返回值。
一個返回本地變量的函數,如下所示:
~~~go
func foo() int {
var i int
defer func() {
i++
}()
return i
}
~~~
上面的函數,返回一個局部變量,同時defer函數也會操作這個局部變量。對于匿名返回值來說,可以假定仍然有一個變量存儲返回值,假定返回值變量為”anony”,上面的返回語句可以拆分成以下過程:
~~~go
anony = i
i++
return
~~~
由于i是整型,會將值拷貝給anony,所以defer語句中修改i值,對函數返回值不造成影響
### 4 主函數擁有具名返回值
主函聲明語句中帶名字的返回值,會被初始化成一個局部變量,函數內部可以像使用局部變量一樣使用該返回值。如果defer語句操作該返回值,可能會改變返回結果。
一個影響函返回值的例子:
~~~go
func foo() (ret int) {
defer func() {
ret++
}()
return 0
}
~~~
上面的函數拆解出來,如下所示:
~~~go
ret = 0
ret++
return
~~~
函數真正返回前,在defer中對返回值做了+1操作,所以函數最終返回1
- 概述
- go語言基礎特性
- Go語言聲明
- Go項目構建及編譯
- go command
- 程序設計原則
- Go基礎
- 變量
- 常量
- iota
- 基本類型
- byte和rune類型
- 類型定義和類型別名
- 數組
- string
- 高效字符串連接
- string底層原理
- 運算符
- new
- make
- 指針
- 下劃線 & import
- 語法糖
- 簡短變量申明
- 流程控制
- ifelse
- switch
- select
- select實現原理
- select常見案例
- for
- range
- range實現原理
- 常見案例
- range陷阱
- Goto&Break&Continue
- Go函數
- 函數
- 可變參數函數
- 高階函數
- init函數和main函數
- 匿名函數
- 閉包
- 常用內置函數
- defer
- defer常見案例
- defer規則
- defer與函數返回值
- defer實現原理
- defer陷阱
- 數據結構
- slice
- slice內存布局
- slice&array
- slice底層實現
- slice陷阱
- map
- Map實現原理
- 集合
- List
- Set
- 線程安全數據結構
- sync.Map
- Concurrent Map
- 面向對象編程
- struct
- 匿名結構體&匿名字段
- 嵌套結構體
- 結構體的“繼承”
- struct tag
- 行為方法
- 方法與函數
- type Method Value & Method Expressions
- interface
- 類型斷言
- 多態
- 錯誤機制
- error
- 自定義錯誤
- panic&recover
- reflect
- reflect包
- 應用示例
- DeepEqual
- 反射-fillObjectField
- 反射-copyObject
- IO
- 讀取文件
- 寫文件
- bufio
- ioutil
- Go網絡編程
- tcp
- tcp粘包
- udp
- HTTP
- http服務
- httprouter
- webSocket
- go并發編程
- Goroutine
- thread vs goroutine
- Goroutine任務取消
- 通過channel廣播實現
- Context
- Goroutine調度機制
- goroutine調度器1.0
- GMP模型調度器
- 調度器竊取策略
- 調度器的生命周期
- 調度過程全解析
- channel
- 無緩沖的通道
- 緩沖信道
- 單向信道
- chan實現原理
- 共享內存并發機制
- mutex互斥鎖
- mutex
- mutex原理
- mutex模式
- RWLock
- 使用信道處理競態條件
- WaitGroup
- 工作池
- 并發任務
- once運行一次
- 僅需任意任務完成
- 所有任務完成
- 對象池
- 定時器Timer
- Timer
- Timer實現原理
- 周期性定時器Ticker
- Ticker對外接口
- ticker使用場景
- ticker實現原理
- ticker使用陷阱
- 包和依賴管理
- package
- 依賴管理
- 測試
- 單元測試
- 表格測試法
- Banchmark
- BDD
- 常用架構模式
- Pipe-filter pattern
- Micro Kernel
- JSON
- json-內置解析器
- easyjson
- 性能分析
- gc
- 工具類
- fmt
- Time
- builtin
- unsafe
- sync.pool
- atomic
- flag
- runtime
- strconv
- template