多个defer的执行顺序为“后进先出”
defer、return、返回值的执行逻辑:
- return最先执行,return负责将结果写入返回值中;
- 接着defer开始执行一些收尾工作;
- 最后函数携带当前返回值退出
代码验证
package main
import "fmt"
func main() {
fmt.Println("func1 return: ", func1()) //1000
fmt.Println("func2 return: ", func2()) //1002
fmt.Println("func3 return: ", *(func3())) //2
}
func func1() int {
var i int
defer func() {
i++
fmt.Println("func1 defer1: ", i)
}()
defer func() {
i++
fmt.Println("func1 defer2: ", i)
}()
i = 1000
return i
}
func func2() (i int) {
defer func() {
i++
fmt.Println("func2 defer1: ", i)
}()
defer func() {
i++
fmt.Println("func2 defer2: ", i)
}()
i = 1000
return i
}
func func3() *int {
var i int
defer func() {
i++
fmt.Println("func3 defer1:", i) // 打印结果为 func3 defer1: 2
}()
defer func() {
i++
fmt.Println("func3 defer2:", i) // 打印结果为 func3 defer2: 1
}()
return &i
}
输出如下:
func1 defer2: 1001
func1 defer1: 1002
func1 result: 1000
func2 defer2: 1001
func2 defer1: 1002
func2 result: 1002
func3 defer2: 1
func3 defer1: 2
func3 result: 2
分析
return会将返回值先保存起来,对于无名返回值来说,保存在一个临时对象中,defer是看不到这个临时对象的;而对于有名返回值来说,就保存在已命名的变量中.
func1()int
函数的返回值没有被提前声名,其值来自于其他变量的赋值,而defer中修改的也是其他变量,而非返回值本身,因此函数退出时返回值并没有被改变。
func2()(i int)
函数的返回值被提前声名,也就意味着defer中是可以调用到真实返回值的,因此defer在return赋值返回值 i 之后,再一次地修改了 i 的值,最终函数退出后的返回值才会是defer修改过的值。
func3()*int
的返回值没有被提前声明,但是由于 c()*int 的返回值是指针变量,那么在return将变量 i 的地址赋给返回值后,defer再次修改了 i 在内存中的实际值,因此函数退出时返回值虽然依旧是原来的指针地址,但是其指向的内存实际值已经被成功修改了。