简析go语言defer关键字后表达式求值时机

在go中,只有在函数和方法内部才能使用defer;defer关键字后面只能接函数或者方法,这些函数被称为deferred函数。defer将他们注册到其所在goroutine用于存放deferred函数的栈数据结构中,这些deferred函数将在执行defer的函数退出前按后进先出(LIFO)的顺序调度执行。无论是执行到函数体尾部返回,还是在某个错误处理分支显式调用return返回,抑或出现panic,已经存储到deferred函数栈中的函数都会被调度执行。

defer关键字后表达式求值时机如何?下面看个例子:

func foo1() {
	for i := 0; i <= 3; i++ {
		defer fmt.Println(i)
	}
}

func foo2() {
	for i := 0; i <= 3; i++ {
		defer func(n int) {
			fmt.Println(n)
		}(i)
	}
}

func foo3() {
	for i := 0; i <= 3; i++ {
		defer func() {
			fmt.Println(i)
		}()
	}
}

func main() {
	fmt.Println("foo1:")
	foo1()
	fmt.Println("foo2:")
	foo2()
	fmt.Println("foo3:")
	foo3()
}

执行结果如下:

foo1:
3
2
1
0
foo2:
3
2
1
0
foo3:
4
4
4
4

分析以上执行结果:

在foo1中,defer后面是fmt.Println函数,每当defer将fmt.Println注册的deferred函数栈的时候,都会对fmt.Println后边的参数进行求值,故依次压入deferred函数栈是:fmt.Println(0) fmt.Println(1) fmt.Println(2) fmt.Println(3)。在foo1执行到函数体尾部,deferred函数被执行,根据LIFO原则,因此输出3 2 1 0。

在foo2中,defer后面是一个带参数的匿名函数,每当defer将匿名函数注册的deferred函数栈的时候,都会对匿名函数的参数进行求值,故依次压入deferred函数栈是: func(0) func(1) func(2) func(3)。在foo2执行到函数体尾部,deferred函数被执行,根据LIFO原则,因此输出3 2 1 0。

在foo3中,defer后面是一个不带参数的匿名函数,故依次压入deferred函数栈是: func() func() func() func()。在foo3执行到函数体尾部,deferred函数被执行,匿名函数以闭包的方式访问外围函数的变量,此时i=4,根据LIFO原则,因此输出4 4 4 4。

结论:defer关键字后边的表达式实在将deferred函数注册到deferred函数栈的时候进行求值的!

Go语言基础及实战 文章被收录于专栏

Go语言学习笔记、语法知识、技术要点和个人理解及实战

全部评论
学点新知识
点赞 回复 分享
发布于 2023-03-26 11:54 山东
狠狠学到了
点赞 回复 分享
发布于 2023-03-26 11:48 甘肃

相关推荐

05-23 20:31
已编辑
武汉大学 Java
内向的柠檬精在研究求职打法:注意把武大标粗标大 本地你俩不是乱杀
点赞 评论 收藏
分享
qq乃乃好喝到咩噗茶:院校后面加上211标签,放大加粗,招呼语也写上211
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务