1. 实现原理

在Go1.14的编译器中,会把函数中的defer语句直接插入到函数尾部,在函数内直接展开defer语句并调用

  1. func f() {
  2. defer fmt.Println("A")
  3. fmt.Println("B")
  4. return
  5. }
  1. func f() {
  2. fmt.Println("B")
  3. fmt.Println("A")
  4. return
  5. }

2. 经典例子

思考一下,以下函数分别输出什么?

  1. func f1() int {
  2. i := 0
  3. defer func(){
  4. i++
  5. }()
  6. return i
  7. }
  8. func f2() (i int) {
  9. defer func(){
  10. i++
  11. }()
  12. return i
  13. }

按照刚才说的原理,应该输出1,1。实际上却输出了0,1
为什么?

3. 与return之间的执行顺序

return的执行是分两步的,第一步,赋值给返回变量;第二步,返回返回变量,而defer是在第一步之后执行。

现在来看看f1,在执行return i时,由于是匿名返回,所以会创建一个临时变量并把 i 赋值给它,temp = i,
然后执行defer中的i++,最后返回temp,因为返回的temp,所以i修改不影响返回,输出0

再看看f2,执行return i,由于是显示命名的返回值,不会创建临时变量,也不需要再赋值;然后执行defer中的i++,最后返回i,因为返回的i,所以defer中对i的修改是会影响到返回的,输出1

同理,如果返回的是指针类型*int,即时f1返回的是临时变量,因为指针执行的是同一块内存,所以取出的值是
defer中修改过的

  1. func f1() *int {
  2. i := 0
  3. defer func(){
  4. i++
  5. }()
  6. return &i
  7. }
  8. func main() {
  9. r := f1()
  10. fmt.Println(*r)
  11. }
  12. // 输出1