一文带你了解defer语句

114 阅读3分钟

什么是defer

Go语言中,defer 是一种用于延迟执行函数调用的关键字。通过使用 defer,我们可以将函数调用推迟到包含 defer 语句的函数即将返回之前执行。无论函数是通过正常返回还是通过发生错误而返回,defer 语句都会被执行。

func count(x int) {
   fmt.Printf("倒计时: %d\n", x)
}

func TestDefer(T *testing.T) {
   defer count(1)
   defer count(2)
   defer count(3)
   fmt.Println("开始倒计时")
}

如上述代码最终会输出:

开始倒计时
倒计时: 3
倒计时: 2
倒计时: 1

Go 语言中,如果有一个 defer 语句定义在 return 语句之前,那么在执行 return 到函数最终返回之前,defer 语句中的代码会按照后进先出(LIFO,Last In, First Out)的顺序执行。即先定义后执行,这意味着最后一个进入的 defer 语句会最先执行,然后是倒数第二个,以此类推,直到最先进入的 defer 语句执行。

defer与函数return

当一个函数中既包含了 defer 语句又有 return 语句时,它们的执行顺序如下:

  • 当函数执行到 defer 语句时,会将 defer 后面的表达式或函数调用压入一个栈中,但不会立即执行。这样做的目的是为了延迟函数的执行,将其放在函数返回之后执行。
  • 接着,函数会执行 return 语句,并将返回结果保存下来。请注意,这里的返回结果并不是最终的函数返回结果,而是在 return 语句中指定的结果。
  • 最后,函数会从栈中按照后进先出(LIFO)的顺序取出被推迟的函数调用,并执行它们。这意味着 defer 语句中的函数调用会在函数返回之后执行。

通过defer修改返回值

函数执行return时,返回结果不一定是最终的结果,他有可能被return之前的defer修改

  • 示例1
func demo1() *int {
   a := 7
   defer func() {
      a++
   }()
   return &a
}

func demo2() *int {
   var a *int = new(int)
   defer func() {
      *a++
   }()
   return a
}

demo1中函数返回了变量a的地址,defer中对a的值+1,最终返回值变量地址的引用,所以最终结果取值会是8;demo2也是同理,最终返回取值会是1

  • 示例2
func demo3() int {
   a := 7
   defer func() {
      a++
   }()
   return a
}

func demo4() int {
   var a *int = new(int)
   defer func() {
      *a++
   }()
   return *a
}

这里demo3demo4会返回7和0,这是因为在defer执行前,返回值已经分别被赋为7和0,在defer中再去对函数内局部变量做修改,并不会改变return的值

  • 示例3
func demo5() (x int) {
   defer func() {
      x++
   }()
   return 7
}

这里demo5会返回8,它和demo3的区别在于,demo5的返回值在函数定义是声明了变量名,defer对返回值进行自增,最后返回的为自增后的值

总结

  • 如果函数返回的是变量的指针,在return之前的defer对变量值进行了修改,最终返回的是指向修改后变量的指针
  • 如果函数返回的是未命名的变量值,在return之前的defer对变量值进行修改,最终返回的还会是修改前的值
  • 如果函数返回的是已命名的变量值,在return之前的defer对变量值进行修改,最终返回的是修改后的值