七叶笔记 » golang编程 » Golang中defer与return的执行顺序

Golang中defer与return的执行顺序

问题

defer在return之前执行,还是在return之后执行???

关于defer延时函数调用,以及先进后出的使用,所有人都已经非常熟悉了,但是这个defer到底是先于return执行,还是在return之后才会执行呢?相信还是有一些同学跟我一样有这样那样的疑问。

先来看两个示例:

 package main

import (
	"fmt"
)

// AnonymousReturnValue 返回值是匿名的函数
func AnonymousReturnValue() (int, *int) {
	var i int // 零值是0

	defer func() { // 匿名函数
		i++
		fmt.Println("AnonymousReturnValue 返回值匿名 defer1:", i, &i)
	}()

	defer func() { // 匿名函数
		i++
		fmt.Println("AnonymousReturnValue 返回值匿名 defer2:", i, &i)
	}()

	return i, &i
}

// NamedReturnValue 返回值是有名的函数
func NamedReturnValue() (i int, addr *int) {
	defer func() { // 匿名函数
		i++
		fmt.Println("NamedReturnValue 返回值匿名 defer1:", i, &i)
	}()

	defer func() { // 匿名函数
		i++
		fmt.Println("NamedReturnValue 返回值匿名 defer2:", i, &i)
	}()

	return i, &i
}

func main() {

	anonymous, addr1 := AnonymousReturnValue()
	fmt.Println("AnonymousReturnValue 的返回值及地址:", anonymous, addr1)
	named, addr2 := NamedReturnValue()
	fmt.Println("NamedReturnValue 的返回值及地址:", named, addr2)
}
  

输出结果:

 AnonymousReturnValue 返回值匿名 defer2: 1 0xc00001c0c0
AnonymousReturnValue 返回值匿名 defer1: 2 0xc00001c0c0
AnonymousReturnValue 的返回值及地址: 0 0xc00001c0c0
NamedReturnValue 返回值匿名 defer2: 1 0xc00001c0d8
NamedReturnValue 返回值匿名 defer1: 2 0xc00001c0d8
NamedReturnValue 的返回值及地址: 2 0xc00001c0d8  

问题出现了???

在这里出现了一个问题, 一个是 返回值内名的函数、 一个是 返回值具名的函数,其他完全一样,为什么会出现两个完全不一样的结果呢?

如果是defer先执行:都应该返回3

如果是return先执行:都应该返回0

不管是defer先执行还是return先执行都不应该出现这样的结果啊?为什么一个是0,一个是3呢

答案

这里边的答案还是在return原理上。return其实包含两个步骤:

第一步:是给返回值赋值(若为有名返回值则直接赋值,若为匿名返回值则先声明再赋值。

第二步:是调用RET返回指令并传入返回值,而RET则会检查defer是否存在,若存在就先逆序插播defer语句,最后RET携带返回值退出函数。

另外:defer则恰恰是在return两个步骤的中间执行,并且defer是匿名函数,拥有匿名函数捕获变量列表的特性。关于匿名函数及闭包请看文章 。

相关文章