go的return和defer

举报
仙士可 发表于 2023/06/30 12:18:52 2023/06/30
【摘要】 示例一  defer的固定传参package mainimport "fmt"func main() { fmt.Println("主函数输出:", test())}func test() int { res := 1000 defer fmt.Println("defer输出:", res) res += 1000 return res}复制以上输出为:原因是:defe...

示例一  defer的固定传参

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函数确定

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 固定地址参数

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执行步骤

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

示例五

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函数无法操作返回值

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

func test() int {
   i := 0
   defer func() {
      i++
   }()
   return 1
}
复制

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

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

func test() int {
   i := 0
   defer func() {
      i++
      fmt.Println(i)
   }()
   return i
}
复制

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

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。