Go语言defer

Posted gyyyl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go语言defer相关的知识,希望对你有一定的参考价值。

函数中被defer关键字声明的语句会被延迟执行,延迟到函数结束之前才执行。

首先对于函数中的return语句,它是由两步组成,而非一个原子操作:

  return=赋值给返回值+返回

func f1(x int) int {  //x=5
	defer func() {
		x++
	}()
	return x
}

  如果传入的参数是5,这个函数的返回值是5,在函数最后返回的时候,首先创建一个匿名变量作为返回值,并且将x的值赋值给这个匿名变量,然后defer的延迟执行函数将x+1,但是此时已经和返回值没有关系了,因为x和返回值是两个变量,最后就是执行RET返回,所以一个函数的return语句是分两步执行,然后defer声明的语句则延迟到这两句之间执行

func f1(x int) (y int) {  //x=5
	defer func() {
		y++
	}()
	return x
}

  如果这个时候传入的参数是5,这个函数的返回值是6,此时我们已经将返回值由匿名变量显示的变为变量y,所以执行的过程为首先将x的赋值给y,此时y的值是5,然后执行defer声明的语句,将y++,所以返回值这个变量的值变为了6,最后执行RET返回,因此最后的返回值是6.

func f1() (y int) {
	defer func(y int) {
		y++
	}(5)
	return y
}

  这个函数的返回值是0,首先返回值y的类型是int,默认值是0,所以在返回的时候首先给y赋值为0,然后执行defer声明的函数,此时采用的是值传递,所以这个函数中的y其实是函数返回值的那个y的副本,他们是两个变量,所以此时修改y不能对函数返回值的那个y起作用,最后再返回。

func f1() (y int) {
	defer func(y *int) {
		*y++
	}(&y)
	return 5
}

  这个函数最后的返回值是6,过程如下,首先对函数的返回值y赋值为5,然后执行defer声明的函数,这个函数的入口参数是一个指针,所以采用的是地址传递而不是值传递,上面那个函数就是把返回值y的地址传入了defer的函数,所以最后修改了返回值y的值,y+1,所以最后返回值是6

  然后再看一个比较复杂的例子,这个例子说明了在对defer的语句进行压栈的时候,会保存当前的状态:

func calc(index string, a, b int) int {
	ret := a + b
	fmt.Println(index, a, b, ret)
	return ret
}

func main() {
	a, b := 1, 2
	// a=1
	// b=2
	defer calc("1", a, calc("10", a, b))
	// defer的函数中还调用了函数,所以在对这条语句压栈的时候,会先执行内层的calc函数
	// calc("10",1,2)==>输出"10" 1 2 3
	// 然后对defer语句当前的状态压栈,当前a=1
	// 所以压栈的语句为:
	// defer calc("1",1,3)
	a = 0
	// a=0
	defer calc("2", a, calc("20", a, b))
	// defer的函数中还调用了函数,所以在对这条语句压栈的时候,会先执行内层的calc函数
	// calc("20",0,2)==>输出"20" 0 2 2
	// 然后对defer语句当前的状态压栈,当前a=0
	// 所以压栈的语句为:
	// defer calc("2",0,2)
	b = 1
	// b=1
	// 然后执行压栈的defer语句
	// calc("2",0,2)==>输出"2" 0 2 2
	// calc("1",1,3)==>输出"1" 1 3 4
	// 所以最后的输出结果是:
	// "10" 1 2 3
	// "20" 0 2 2
	// "2" 0 2 2
	// "1" 1 3 4
}

  从上面的代码可以看出两点,defer声明的函数内部如果还有函数调用(比如参数是另一个函数),那么会首先执行那个函数,并且得到一个结果,然后将这个结果压栈,另一点就是对于defer函数的变量,会在defer被压栈的时候保存这些变量当前的值,defer语句被压栈以后,再修改这些变量的值,并不会对defer语句中的这些变量产生影响。

以上是关于Go语言defer的主要内容,如果未能解决你的问题,请参考以下文章

Go语言开发Go语言错误处理

Go语言中异常处理painc()和recover()的用法

go语言的defer语句

go语言的defer语句

Go语言中异常处理painc()和recover()的用法

Go语言之Go语言 异常处理与测试