前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >go的return和defer

go的return和defer

作者头像
仙士可
发布2022-09-13 18:25:02
2570
发布2022-09-13 18:25:02
举报
文章被收录于专栏:仙士可博客仙士可博客

示例一  defer的固定传参

代码语言:javascript
复制
package main

import "fmt"

func main() {
   fmt.Println("主函数输出:", test())
}

func test() int {
   res := 1000
   defer fmt.Println("defer输出:", res)
   res += 1000
   return res
}

以上输出为:

仙士可博客
仙士可博客

原因是:defer 函数的参数在定义的时候就以及确定了(形参拷贝),所以后面就算修改了值也不会发生变化

示例二 defer函数确定

代码语言:javascript
复制
package main

import "fmt"

func main() {
   fmt.Println("主函数输出:", test())
}

func test() int {
   res := 1000
   defer func() {
      fmt.Println("defer输出:", res)
   }()
   res += 1000
   return res
}

以上输出为:

仙士可博客
仙士可博客

原因是 defer只确定了一个匿名函数地址,匿名函数进行第二次的调用,在匿名函数确定好的时候,确定了res的地址,在执行匿名函数之后才开始获取res值然后进行形参传递输出,所以输出2000

示例三 defer 固定地址参数

代码语言:javascript
复制
package main

import "fmt"

func main() {
   fmt.Println("主函数输出:")
   deferFunction()
}

func deferFunction() {
   var arr = []int{1, 2, 3}
   defer fmt.Println("deferFunction:", arr)
   arr[0] = 6
   return
}

输出:

仙士可博客
仙士可博客

在defer时,确定了arr的地址(数组是地址形式,直接传递地址),所以在打印时,可以打印到更改的数据

示例四  defer+return执行步骤

代码语言:javascript
复制
package main

import "fmt"

func main() {
   fmt.Println("主函数输出:", deferFunction())
}

func deferFunction() (result int) {
   result = 1
   defer func() {
      result = 2
   }()
   return
}

输出:

仙士可博客
仙士可博客

原因是:

函数调用的执行步骤为: 调用函数->设定返回值result->赋值result=1->准备return,return的值为resulr->执行defer赋值为2->return执行完毕,正式返回

所以输出2

示例五

代码语言:javascript
复制
package main

import "fmt"

func main() {
   fmt.Println("主函数输出:", deferFunction())
}

func deferFunction() (result int) {
   i := 1
   defer func() {
      result = 2
   }()
   return i
}

输出:

仙士可博客
仙士可博客

原因是:

return的调用并非原子性的,分为2个步骤:1 确定返回值,2正式返回

在确定返回值之后,会去执行defer方法,如果defer将返回值变更,则返回时数据也会变更.

在此示例中,return将i赋值给了result,这个时候result=1,同时result又更改成了2,所以为2

总结

规则一:延迟函数的参数在声明时就确定下来了

defer函数在声明时就已经确定好了参数,并且形参做了一次值拷贝,成为了一个新值

这个规则对于指针类型也同样适用,相当于拷贝了一份指针,但是指针指向的值确实实实在在变了的

规则二:延迟函数执行按后进先出顺序执行,即先出现的defer最后执行

defer函数在声明后类似于入栈操作,调用时候类似于出栈操作,所以是后进先出执行

规则三:函数返回过程非原子操作

return时非原子操作的,return操作的是将返回值存入栈中等待操作,

然后执行返回跳转

但是在执行返回跳转操作时,还需要执行defer函数,所以在defer函数中可以操作这个返回值

但是在特殊情况下,defer函数无法操作返回值

特殊情况一:函数有匿名返回值,直接返回字面量

代码语言:javascript
复制
func test() int {
   i := 0
   defer func() {
      i++
   }()
   return 1
}

该函数直接将1写入返回值,所以defer无法操作返回值

特殊情况二:函数有匿名返回值,返回变量

代码语言:javascript
复制
func test() int {
   i := 0
   defer func() {
      i++
      fmt.Println(i)
   }()
   return i
}

该情况下,defer可以引用到i的变量值,进行一次值拷贝,所以defer操作的是拷贝后的i值,不会发生变化(如果返回变量类型为指针类型,则会发生变化)

本文为仙士可原创文章,转载无需和我联系,但请注明来自仙士可博客www.php20.cn

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-08-31 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 示例一  defer的固定传参
  • 示例二 defer函数确定
  • 示例三 defer 固定地址参数
  • 示例四  defer+return执行步骤
  • 示例五
  • 总结
    • 规则一:延迟函数的参数在声明时就确定下来了
      • 规则二:延迟函数执行按后进先出顺序执行,即先出现的defer最后执行
        • 规则三:函数返回过程非原子操作
          • 特殊情况一:函数有匿名返回值,直接返回字面量
          • 特殊情况二:函数有匿名返回值,返回变量
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档