defer和return的执行顺序探究
先来看一段代码
package main
import (
"fmt"
)
func f() int{
var i int
defer func(){
i++
}()
return i
}
func f2() (i int){
defer func(){
i++
}()
return i
}
func main() {
fmt.Println(f())
fmt.Println(f2())
}
上面的程序输出结果是啥呢?答案是:0 1
你答对了嘛?
这里面涉及到的就是defer和return 执行顺序的关系。
我们先来探究一下defer执行的顺序。
defer 的执行顺序是按照先声明后执行的栈顺序执行。来个例子
package main
import (
"fmt"
)
func main() {
defer func(){
fmt.Println("f1")
}()
defer func(){
fmt.Println("f2")
}()
fmt.Println("Hello fudan!")
}
执行结果为:
Hello fudan!
f2
f1
所以大家通过这个例子应该知道了defer的执行顺序,那如果多个defer之间多了几个语句呢?
再来看一个例子:
package main
import (
"fmt"
)
func main() {
defer func(){
fmt.Println("f1")
}()
fmt.Println("You")
defer func(){
fmt.Println("f2")
}()
fmt.Println("Me")
fmt.Println("Hello fudan!")
}
执行结果是:
You
Me
Hello fudan!
f2
f1
如果上面两个例子都明白了,那defer基本就大概知道是什么原理了。那接下来来看一下return 的基本执行过程吧。
return 的基本执行过程
我们知道go中的函数返回值有匿名和有名两种。
- 对于匿名的可以理解成执行return 语句的时候,分成两步,第一步需要设置一个临时变量s用来接收返回值,第二步将临时变量s返回。
- 对于有名的可以理解成执行return的时候,直接将变量返回。
而我们知道,所有的defer都将在真正的return 变量之前运行,所以对于上面两种情况,defer对于返回值的影响也有两种:
- 对于匿名的:第一步设置临时变量保存返回值,第二步按照defer的执行步骤执行defer语句,如果其中有对变量的修改,将不会影响s变量的值。
- 对于有名的:第一步先执行defer,对变量进行修改,第二步,返回被修改的返回值。
所以,理解了上面的步骤的之后,我们就可以理解开头的函数了:
package main
import (
"fmt"
)
func f() int{
var i int
defer func(){
i++
}()
return i
}
func f2() (i int){
defer func(){
i++
}()
return i
}
func main() {
fmt.Println(f())
fmt.Println(f2())
}
分析:
执行return 的时候,
- f函数先通过临时变量s保存i的值,此时i为0,所以s=0, 然后执行defer,修改i的值,i变为1,最后返回s的值,因为s=0, 所以返回值是0;
- 而f2是有名函数,所以执行return 的时候,i被设置为1,然后真正的返回i值,所以返回的是1.
所以最后的执行结果为:0 1
好了,关于defer和return 就先讲到这了。加油加油!