![f6e40c317630f8699705260884e61a00.png](https://i-blog.csdnimg.cn/blog_migrate/9b3e5429121cf8b4a3b1483b0bbed2ef.png)
对 Golang 感兴趣的同学欢迎关注公众号:golang-experts
defer 估计是每个 Gopher 每天写代码都会写,那么你是不是真正的理解了 defer 呢?不妨看一下下面这个代码片段,这个是我之前给 UC 那边一个 team 做 Golang 培训的时候想的例子。
package main
func f() int {
i := 5
defer func() {
i++
}()
return i
}
func f1() (result int) {
defer func() {
result++
}()
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 main() {
println(f())
println(f1())
println(f2())
println(f3())
}
1. return 语句
在解析上面的题目之前,要理解一个前提是 Go 的函数返回值是通过堆栈返回的,这也是实现了多返回值的方法。举个例子。
//foo.go
package main
func foo() (int,int){
i := 1
j := 2
return i,j
}
func main() {
foo()
}
查看汇编代码如下。
$ go build -gcflags '-l' -o foo foo.go
$ go tool objdump -s "main.foo" foo
TEXT main.foo(SB) /Users/kltao/code/go/src/example/foo.go
bar.go:6 0x104ea70 48c744240801000000 MOVQ $0x1, 0x8(SP)
bar.go:6 0x104ea79 48c744241002000000 MOVQ $0x2, 0x10(SP)
bar.go:6 0x104ea82 c3 RET
也就是说 return 语句不是原子操作,而是被拆成了两步
rval = xxx
ret
而 defer 语句就是在这两条语句之间执行,也就是
rval = xxx
defer_func
ret
另外在 Go 语言的 func 声明中如果返回值变量显示声明,也就是 func foo() (ret int) {}
的时候,rval 就是 ret。这么上面的题目中对于的函数执行简单来说就是如下代码片段。但是 f3 涉及到另外一个知识点,也就是闭包。
//f
rval = i
i ++
ret
//f1
result = 0
defer // result ++
return
//f2
r = t
defer // t = t + 5
return
2. 闭包
简单来说,Go 语言中的闭包就是在函数内引用函数体之外的数据,这样就会产生一种结果,虽然数据定义是在函数外,但是在函数内部操作数据也会对数据产生影响。如下面的例子所示,foo() 中的匿名函数对 i 的调用就是闭包引用,i++ 会影响外面定义的 i 的值。而 bar() 中的匿名函数是变量拷贝,i++ 并不会修改外部 i 值。这么看的话,开始的 f3() 的输出你是不是知道是多少了呢?
func foo() {
i := 1
go func() {<