怎么才能深刻理解defer和return之间关系呢?细读本文后将对你有很大帮助。
一、理解基本概念
-
go函数中return不是原子操作,在编译器中分解为两个部分:返回值赋值和return, 而defer则刚好被插入到末尾的return之前执行,因此可以在defer函数中修改返回值。
-
多个defer的执行顺序为LIFO(后进先出)
-
defer、return、返回值三者的执行逻辑应该是:return最先执行,return负责将结果写入返回值中;接着defer开始执行一些收尾工作;最后函数携带当前返回值退出。
然后尝试解答以下示例,本文末尾会有答案和解析说明,手动写下答案后,对比本文末尾答案或者直接运行查看结果是否和手写的答案一样呢?
二、开始做题
- 示例1
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())
}
- 示例2
func f1() (r int) {
defer func() {
r++
}()
return 0
}
func f2() (r int) {
t := 5
defer func() {
t = t + 5
}()
return t
}
func f3() (r int) {
defer func(r int) {
r = r + 5
}(r)
return 1
}
func f4() (r int) {
t := 5
defer func() {
r = t + 5
}()
return t
}
- 示例3
var a bool = true
func main() {
defer func(){
fmt.Println("1")
}()
if a == true {
fmt.Println("2")
return
}
defer func(){
fmt.Println("3")
}()
}
- 示例4
func calc(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret)
return ret
}
func main() {
a := 1
b := 2
defer calc("1", a, calc("10", a, b))
a = 0
defer calc("2", a, calc("20", a, b))
b = 1
}
建议手动写下答案,如果有模糊的地方,然后在返回基础概念模块,在认真读一下。
三、答案及解析
- 示例1
输出结果: 0 1
分析说明:好好理解一下概念3。以匿名返回值方法(increaseA())举例
发生return时,将赋值给返回值(可以理解为go自动创建了一个返回值retValue,相当于执行了retValue=value),此时defer修改该的是value而不是retValue。
而在命名返回值函数中,由于返回值在方法定义时已经被定义,所以不会在创建retValue了,defer对value的修改也会被直接返回。
- 示例2
输出结果: 1, 5, 1, 10
分析说明: 概念3, f3() 变量作用域问题。
- 示例3
输出结果: 2 1
分析说明: 关键字后面的函数或者方法想要执行必须先注册,return 之后的 defer 是不能注册的, 也就不能执行后面的函数或方法。
- 示例4
输出结果:
10 1 2 3
20 0 2 2
2 0 2 2
1 1 3 4
分析说明: 不管代码顺序如何,defer calc func中参数b必须先计算,故会在运行到第三行时,执行calc("10",a,b)输出:10 1 2 3得到值3,将cal("1",1,3)存放到延后执执行函数队列中。
执行到第五行时,现行计算calc("20", a, b)即calc("20", 0, 2)输出:20 0 2 2得到值2,将cal("2",0,2)存放到延后执行函数队列中。
执行到末尾行,按队列先进后出原则依次执行:cal("2",0,2)、cal("1",1,3) ,依次输出:2 0 2 2、1 1 3 4 。
网友评论