go的return和defer

示例一  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值,不会发生变化(如果返回变量类型为指针类型,则会发生变化)



仙士可博客
请先登录后发表评论
  • 最新评论
  • 总共0条评论
  • 本站由白俊遥博客程序搭建
    © 2017-1-17 php20.cn 版权所有 ICP证:闽ICP备17001387号
  • 本网站由: 提供cdn加速/云存储服务
  • 联系邮箱:1067197739@qq.com