defer函数参数求值简要分析
引子
书接上文,在发表了 这里显然有坑!初学者的常规逻辑一般是:defer是在main函数退出后执行,退出前i已经做了+1操作,值变成了2,这样一来defer后的Println应该输出:result => 4 才对!实际输出结果呢? 这怎么可能? 实际上不光是defer这样,即使用go关键字替换掉defer,输出的结果也是一样的:result => 2 那么究竟为什么输出的是2,而不是4呢?因为无论是go关键字还是defer关键字,在代码执行到它们时,编译器都要为它们后面的函数准备好函数调用的参数堆栈,要确定的参数值和参数类型大小。这样一来就得去求值:对它们后面的函数的参数进行求值。 以本文第一个defer那个例子为例!我们需要为defer后面的函数进行 此时defer后面的函数是Println,这里Println有两个输入参数:”result =>”和func() int {return i * 2}(),前者就是一个字符串常量值,而后者是一个函数调用,我们需要对该函数调用进行求值。而在此时,i依然为1,因此Println的第二个参数的求值结果为2,于是上面defer的调用就等价于: 因此,无论最终i的值变成了多少,defer最终的输出都是:result => 2。go关键字后面的参数亦是如此。其实这个过程与为普通函数的调用做准备是一样的,也要先对函数的参数进行求值,之后再进入函数体,只不过defer将进入函数执行的过程推迟到defer的调用方退出之前了。 搞清楚这个defer原理后,我们如果想在defer函数执行时输出4,那么使用一个闭包函数即可: 这里我们看到defer 后面是一个不带任何参数的匿名函数,所谓的对参数求值也是无值可求。在main函数退出前,defer后面的匿名函数真正执行时i的值已经是2,因此闭包函数中的Println输出4。 defer后面除了可以跟着普通函数调用外,还可以使用方法调用(method): 这可能又会让初学者有些迷惑,多数又是Method的receiver类型以及go自动对instance的Method调用解引用或求地址的问题,我们“趁热打铁”,再来基于上一篇文章 这段代码运行起来输出:result => 2
package mainimport ( "fmt"
"time")func main() {
var i int = 1
go fmt.Println("result =>",func() int { return i * 2 }())
i++
time.Sleep(3*time.Second)
}
defer function分析
defer fmt.Println("result =>",2)
// https://play.golang.org/p/Eux7zpSr7O8package mainimport "fmt"func main() {
var i int = 1
defer func() {
fmt.Println("result =>", func() int { return i * 2 }())
}()
i++
}
defer method分析
defer instance.Method(x,y)
six
six
six
three
two
one
共同學習,寫下你的評論
評論加載中...
作者其他優質文章