defer中去修改函数中的变量时,会出现个小坑:
修改函数中的变量会无效,如下面的函数a().
/*
func main() {
k := a()
fmt.Println("return:", k) // 打印结果为 return: 0
}
func a() int {
var i int
i = 0
defer func() {
i++
fmt.Println("defer2:", i) // 打印结果为 defer: 2
}()
defer func() {
i++
fmt.Println("defer1:", i) // 打印结果为 defer: 1
}()
return i
}
*/
func main() {
k := b()
fmt.Println("return:", k) // 打印结果为 return: 2
}
func b() (i int) {
defer func() {
i++
fmt.Println("defer2:", i) // 打印结果为 defer: 2
}()
defer func() {
i++
fmt.Println("defer1:", i) // 打印结果为 defer: 1
}()
i++
return i // 或者直接 return 效果相同
}
/*
func main() {
fmt.Println("c return:", *(c())) // 打印结果为 c return: 2
}
func c() *int {
var i int
defer func() {
i++
fmt.Println("c defer2:", i) // 打印结果为 c defer: 2
}()
defer func() {
i++
fmt.Println("c defer1:", i) // 打印结果为 c defer: 1
}()
return &i
}
*/
有个概念先解释下:
1.在有defer语句时,return语句并代表整个函数已退出
2.在go文档中有这么句:
Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usualand saved anew but the actual function is not invoked
defer语句出现的地方,值和参数就已经赋值并更新,只不过函数没有被调用
用栈来打个比方。defer函数在函数的栈底(相当有两个栈,defer栈和普通栈,defer栈存放defer函数,同时defer栈在return语句后),defer函数入栈时,会带上相应的环境.
对于 A来说,
return语句点i值确定了(0),defer不会也没办法改变其值,有点像函数的值传递,里面的值是无法改变外面的值的
对于B来说,
return语句点i确定为全局变量i,但是其值是不确定的,要等defer完了之后(既整个函数都执行完),才能确定.
对于C来说,
return语句点只确定了i的地址,值也是不确定的,要等defer完了之后(既整个函数都执行完),通过地址间接获取值
汇编厉害的话,在编译后的汇编中也可以看到,
a的话就是直接赋值,b的话就是全局变量,c会新建个object.
而且defer语句出现的地方,会直接插入一个call runtime.deferproc(SB),也就是前面那句,"defer语句出现的地方,值和参数就已经赋值并更新".
PS:我不懂汇编,自己通过对比汇编文件下理解出来的
go tool compile -S XXX.go 可以获得