Go里面,流程控制语句中有一个defer
的关键字,翻译过来就是延迟的意思。
下面用代码说话。
func deferFunc() {
defer fmt.Println("后打印")
fmt.Println("先打印")
}
defer
在函数执行完毕之后,才会执行,所以,我们经常在释放资源或异常处理等需要收尾的场景下会用到defer
。
不过defer
也有一些“坑”,大家需要注意一下。
func printNumbers() {
for i := 0; i < 5; i++ {
defer func() {
fmt.Print(i) // 输出55555
}()
}
}
上面例子中,defer
是在for
循环全部执行完毕之后才会执行,所以i
已经变成了5
,正确做法是下面这样
func printNumbers() {
for i := 0; i < 5; i++ {
defer func(j int) {
fmt.Print(j) // 输出43210
}(i)
}
}
上面打印的是倒序,这是因为defer
的执行顺序和栈的特性一样,都是先进后出的
defer
还有一个特性,当函数有参数传入时,那些参数的值会在声明时求出
func printNumbers() {
for i := 0; i < 5; i++ {
defer func(j int) {
fmt.Print(j) // 输出86420
}(i * 2)
}
}
上面的例子大家可能看的不是很清楚,下面给过一个例子
func main() {
i := 2
defer fmt.Println(i) // 输出2,原因就是上面说的
i = 8
fmt.Println(i)
}
上面这个例子就很明显了,后面i
如何变都不会影响defer
输出的内容
defer
还有一个经常在面试中提到的问题,就是和return
的执行顺序,下面是我整理的3个例子
func main() {
fmt.Println("a return: ", a())
fmt.Println("b return: ", b())
fmt.Println("c return: ", *c())
}
func a() int {
var i int
defer func() {
i = 1
fmt.Println("a defer i: ", i)
}()
return i
}
func b() (i int) {
defer func() {
i = 2
fmt.Println("b defer i: ", i)
}()
return i
}
func c() *int {
var i int
defer func() {
i = 3
fmt.Println("c defer i: ", i)
}()
return &i
}
// 输出
a defer i: 1
a return: 0
b defer i: 2
b return: 2
c defer i: 3
c return: 3
原因网上有很多优秀的解释,我就再说一下我的理解
-
a
函数之所以返回0
,是因为返回值是无名的,return
把i
的值写入返回值后,defer
再修改i
的值,但是已经影响不到返回值了 -
b
函数之所以返回1
,是因为返回值已经声明成i
了,return
之后,defer
对i
修改自然就影响了return的返回值了 -
c
函数结果和a
函数不一样,是因为返回值是指针类型,一样的道理,返回值改不了,但是返回值指向的值已经更改,所以就有上面的结果
正常工作里面,虽然不可能写这种代码,但是面试的时候这些基础问题出现的频率还是挺高的,所以大家也是需要掌握一下。融汇贯通,自己多操作多试试,加深印象。
有疑问加站长微信联系(非本文作者)