golang defer && return

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 可以获得

    原文作者:大漠狼道
    原文地址: https://www.jianshu.com/p/d37570dd6395
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞