详解Go中defer与return的执行顺序
作者:nil
示例
直接上代码
func test() int { result = 123 defer func() { result = 456 }() return result } func main() { fmt.Println(test()) }
结果
123
修改之后的代码
func test() (result int) { result = 123 defer func() { result = 456 }() return result } func main() { fmt.Println(test()) }
结果
456
再看下面这个例子
func test() (result int) { result = 123 defer func() { fmt.Println("aaa") result = 456 }() return func() int { fmt.Println("bbb") return result }() } func main() { fmt.Println(test()) }
结果
bbb
aaa
456
defer与return哪个先执行
这个问题主要是defer 与return哪个先执行。很容易理解如果一个函数中有多个defer,它是栈的形式保存的,执行的时候先从栈顶执行,即后面定义的defer会先被执行,并且defer是在return执行之后才执行的。
因为defer是在return 执行之后才执行的,所以第三个例子中先打印bbb后打印aaa很好理解。
第二个例子和第三个例子test函数返回456也好理解,因为defer可以改变返回值中定义的变量。虽然return已经返回了,defer还是可以改变它。
第一个例子,defer改变的不是返回值中定义的变量,而是局部变量,这个时候return已经执行了,defer改变局部变量没有用。在defer中改变局部变量的值没有效果。第一个例子return result是值拷贝,即将result的值拷贝一份并返回,因此defer改变result并不会影响返回值。
第二、第三例子中return返回的result不是值拷贝,因为result是在返回值中定义的变量,所以return返回的直接是那个变量,这个时候没有值拷贝
再看看下面这个例子
func test() *int { result := 123 defer func() { result = 456 }() return &result } func main() { fmt.Println(*test()) }
结果
456
这个时候defer改变局部变量result又生效了,这是为什么?是因为return 返回的是局部变量的地址,而不是局部变量的只拷贝。因此在defer中修改局部遍历会影响返回结果。
总结
上面描述可能有点绕,需要亲自实验一下,仔细理解才能真正搞懂。下面总结一下我的理解:
return 返回有2种方式:
值拷贝:将局部变量的值拷贝到返回值上。return 直接返回局部变量的值(不是局部变量的引用)
非值拷贝:即return 返回值的时候没有发生值拷贝,有两种情况:
- 将返回值中定义的变量返回。
- 将局部变量的引用返回。
非值拷贝的情况下,defer修改返回值是生效的。
return 的执行其实用两步骤:1.先将return的结果赋值到返回值上;2.再将返回值赋值作为函数的结果赋值给调用者。
defer的执行是在return的两步骤中间执行的。所以return如果发生了值拷贝则defer不会改变返回结果;如果return没有发生值拷贝则defer会改变返回结果。
到此这篇关于详解Go中defer与return的执行顺序的文章就介绍到这了,更多相关Go defer return内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!