2025-04-20    2025-04-20    1046 字  3 分钟
Go

Table of Contents generated with DocToc

  1. 多个defer的执行顺序为"后进先出"
  2. defer,return,返回值三者的执行逻辑应该是:return最先执行,return负责将结果写入返回值中;接着defer开始一些收尾工作;最后函数携带当前返回值退出.

如果函数的返回值是无名的(不带命名返回值),则go语言会在执行return的时候执行一个类似创建临时变量作为return值的动作,而有名返回值的函数,由于返回值在函数定义的时候已经将该变量进行定义,在执行return的时候会先执行返回值保存工作,而后续的defer函数会改变这个返回值(虽然defer是在return之后执行的,,但是由于使用的函数定义的变量,所以执行defer操作后对该变量的修改会影响到return的值)。

eg1 无命名返回值

import "fmt"  
  
func main() {  
   fmt.Println("return:", test()) // defer 和 return之间的顺序是先返回值, i=0,后defer  
}  
  
func test() int { //这里返回值没有命名  
   var i int  
   defer func() {  
      i++  
      fmt.Println("defer1", i) //作为闭包引用的话,则会在defer函数执行时根据整个上下文确定当前的值。i=2  
   }()  
   defer func() {  
      i++  
      fmt.Println("defer2", i) //作为闭包引用的话,则会在defer函数执行时根据整个上下文确定当前的值。i=1  
   }()  
   return i  
}

test() 先返回 i=0 defer2先于defer1执行

#输出结果为:
defer2 1
defer1 2
return: 0

eg2:带命名返回值的函数:

func main() {  
   fmt.Println("return:", test()) // defer 和 return之间的顺序是先返回值, i=0,后defer  
}  
  
func test()  (i int) { //这里返回值没有命名  
   defer func() {  
      i++  
      fmt.Println("defer1", i) //作为闭包引用的话,则会在defer函数执行时根据整个上下文确定当前的值。i=2  
   }()  
   defer func() {  
      i++  
      fmt.Println("defer2", i) //作为闭包引用的话,则会在defer函数执行时根据整个上下文确定当前的值。i=1  
   }()  
   return i  
}

对外部变量的引用

作为函数参数(i),则在defer申明时就把值传递给defer, 输出结果为:

defer2 1
defer1 2
return: 2

理解return 返回值的运行机制:

为了弄清上述两种情况的区别,我们首先要理解return 返回值的运行机制: return 并非原子操作,分为赋值,和返回值两步操作 eg1 : 实际上return 执行了两步操作,因为返回值没有命名,所以return 默认指定了一个返回值(假设为s),首先将i赋值给s,后续的操作因为是针对i,进行的,所以不会影响s, 此后因为s不会更新,所以return s 不会改变 相当于:

var i int
s := i
return s

eg2 : 同上,s 就相当于 命名的变量i, 因为所有的操作都是基于命名变量i(s),返回值也是i, 所以每一次defer操作,都会更新返回值i