Go关键字defer的特性

Go里面,流程控制语句中有一个defer的关键字,翻译过来就是延迟的意思。
下面用代码说话。

func deferFunc() {
	defer fmt.Println("后打印")
	fmt.Println("先打印")
}

defer在函数执行完毕之后,才会执行,所以,我们经常在释放资源或异常处理等需要收尾的场景下会用到defer

不过defer也有一些“坑”,大家需要注意一下。

func printNumbers() {
	for i := 0; i < 5; i++ {
		defer func() {
			fmt.Print(i) // 输出55555
		}()
	}
}

上面例子中,defer是在for循环全部执行完毕之后才会执行,所以i已经变成了5,正确做法是下面这样

func printNumbers() {
	for i := 0; i < 5; i++ {
		defer func(j int) {
			fmt.Print(j) // 输出43210
		}(i)
	}
}

上面打印的是倒序,这是因为defer的执行顺序和栈的特性一样,都是先进后出

defer还有一个特性,当函数有参数传入时,那些参数的值会在声明时求出

func printNumbers() {
	for i := 0; i < 5; i++ {
		defer func(j int) {
			fmt.Print(j) // 输出86420
		}(i * 2)
	}
}

上面的例子大家可能看的不是很清楚,下面给过一个例子

func main() {
	i := 2
	defer fmt.Println(i) // 输出2,原因就是上面说的

	i = 8
	fmt.Println(i)
}

上面这个例子就很明显了,后面i如何变都不会影响defer输出的内容

defer还有一个经常在面试中提到的问题,就是和return的执行顺序,下面是我整理的3个例子

func main() {
	fmt.Println("a return: ", a())
	fmt.Println("b return: ", b())
	fmt.Println("c return: ", *c())
}

func a() int {
	var i int

	defer func() {
		i = 1
		fmt.Println("a defer i: ", i)
	}()

	return i
}

func b() (i int) {
	defer func() {
		i = 2
		fmt.Println("b defer i: ", i)
	}()

	return i
}

func c() *int {
	var i int
	defer func() {
		i = 3
		fmt.Println("c defer i: ", i)
	}()

	return &i
}
// 输出
a defer i:  1
a return:  0
b defer i:  2
b return:  2
c defer i:  3
c return:  3

原因网上有很多优秀的解释,我就再说一下我的理解

  • a函数之所以返回0,是因为返回值是无名的,returni的值写入返回值后,defer再修改i的值,但是已经影响不到返回值了
  • b函数之所以返回1,是因为返回值已经声明成i了,return之后,deferi修改自然就影响了return的返回值了
  • c函数结果和a函数不一样,是因为返回值是指针类型,一样的道理,返回值改不了,但是返回值指向的值已经更改,所以就有上面的结果

正常工作里面,虽然不可能写这种代码,但是面试的时候这些基础问题出现的频率还是挺高的,所以大家也是需要掌握一下。融汇贯通,自己多操作多试试,加深印象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值