go的return和defer

2022-09-13 18:25:02 浏览数 (1)

示例一  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

0 人点赞