Go语言中defer和return解析

Posted 刘贤松handler

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go语言中defer和return解析相关的知识,希望对你有一定的参考价值。

1、return非原子性

Golang语言中函数的return不是原子操作,在底层是分为两步来执行

        第一步:返回值 赋值

        第二步:真正的RET返回

首先明确return执行前都做了哪些事情。

    return是非原子性的,需要两步,执行前首先要得到返回值(为返回值赋值),return将返回值返回调用处。

函数中如果存在 defer,那么 执行顺序时机是在第一步和第二步之间

        第一步:返回值 赋值

        //  defer

        第二步:真正的RET返回

2、defer、return、返回值之间的关系

例子1:无名返回值(即函数返回值为没有命名的返回值)

package main
 
import (
        "fmt"
)
 
func main() 
        fmt.Println("return:", Demo()) // 打印结果为 return: 0

 
func Demo() int 
        var i int
        defer func() 
                i++
                fmt.Println("defer2:", i) // 打印结果为 defer: 2
        ()
        defer func() 
                i++
                fmt.Println("defer1:", i) // 打印结果为 defer: 1
        ()
        return i

输出结果:

defer2 1 
defer1 2 
return: 0

例子2:有名返回值(函数返回值为已经命名的返回值)

package main
 
import (
        "fmt"
)
 
func main() 
        fmt.Println("return:", Demo2()) // 打印结果为 return: 2

 
func Demo2() (i int) 
        defer func() 
                i++
                fmt.Println("defer2:", i) // 打印结果为 defer: 2
        ()
        defer func() 
                i++
                fmt.Println("defer1:", i) // 打印结果为 defer: 1
        ()
        return i // 或者直接 return 效果相同

 输出结果:

defer2 1 
defer1 2 
return: 2

从上面的测试用例可以看出,返回值,defer,return之间的执行顺序是:
先为返回值赋值,然后执行defer,然后return到函数调用处。
 
测试用例1 : 
实际上return 执行了两步操作。
因为返回值没有命名,所以return 之前
首先默认创建了一个临时零值变量(假设为s)作为返回值
然后将i赋值给s,此时s的值是0。后续的操作是针对i进行的,
所以不会影响s, 此后因为s不会更新,
所以return s 不会改变
    相当于:
          var i int
          s := i
          return s
测试用例2:
因为返回值已经提前定义了,不会产生临时零值变量,
返回值就是提前定义的变量,后续所有的操作也都是基于已经定义的变量,
任何对于返回值变量的修改都会影响到返回值本身。
 
就相当于s就是命名的变量i, 后续所有的操作都是基于
命名变量i(s),返回值也是i, 所以每一次defer操作,
都会更新返回值i。

加强分析举例:


package main 

import (
    "fmt"
)

// Go语言中函数的return不是原子操作,在底层是分为两步来执行
// 第一步:返回值赋值
// defer
// 第二步:真正的RET返回
// 函数中如果存在defer,那么defer执行的时机是在第一步和第二步之间

func f1() int 
    x:=5
    defer  func()
        x++ // 修改的是x不是返回值
    ()
    return x // 1. 返回值赋值 2.defer 3.真正的ret指令


func f2()(x int)
   defer func()
        x++
   ()
   return 5 // 返回值=x


func f3() (y int)
    x:=5
    defer func()
        x++ // 修改的是x
    ()
    return x  // 1. 返回值 = y = x = 5 2. defer修改的是x 3. 真正的返回


func f4()(x int)
   defer func (x int)
       x++ // 改变的是函数中x的副本
   (x)
    return 5 // 返回值 = x = 5


func f5()(x int)
   defer func (x int) int 
       x++
       return x 
   (x)
   return 5 // 1.x = 5 2. defer x = 6 3  真正的返回


func f6()(x int)
   defer func (x *int) *int 
       (*x)++
       return x
   (&x)
   return 5 // 1. x = 5 // 2.defer  x =6  3. ret返回


func main()
   fmt.Println(f1()) // 5
   fmt.Println(f2()) // 6
   fmt.Println(f3()) // 5
   fmt.Println(f4()) // 5
   fmt.Println(f5()) // 5
   fmt.Println(f6()) // 6

 

以上是关于Go语言中defer和return解析的主要内容,如果未能解决你的问题,请参考以下文章

Golang Go语言中的 defer 怎么使用?

Golang详解go语言中的defer

Go语言中defer的一些坑

Go语言学习——彻底弄懂return和defer的微妙关系

Go ---- defer 和 return 执行的先后顺序

Go语言defer