标签:turn 例子 显示 Go语言 并且 一点 延迟 函数调用 赋值
函数中被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语句中的这些变量产生影响。
标签:turn 例子 显示 Go语言 并且 一点 延迟 函数调用 赋值
原文地址:https://www.cnblogs.com/gyyyl/p/12683830.html