defer之间的执行顺序

go 的 defer 语句是用来延迟执行函数的关键字

package main

import "fmt"

func defers(a int) int {
	defer func() {
		fmt.Println("Defer 1...")
	}()
	defer func() {
		fmt.Println("Defer 2...")
	}()
	fmt.Println("Run defers  ...")
	return a
}

func main() {
	res:= defers(1)
	fmt.Printf("get result: %d\n",res)
}

//运行结果
//Run defers  ...
//Defer 2...
//Defer 1...
//get result: 1

由此可见 :

  • 多个defer执行顺序为后进先出,可以推测defer将函数压入栈中,待到执行时在依次出栈执行

未声明返回值

package main

import "fmt"

func demo1(a int) ( int) {
	res := a
	defer func() {
		fmt.Println("Defer ...")
		res += 100
	}()
	return res
}

func main() {
	res := demo1(1)
	fmt.Printf("get result: %d\n", res)
}

//运行结果
//Defer ...
//get result: 1

声明返回值

package main

import "fmt"

func demo2(a int) (res int) {
	res = a
	defer func() {
		fmt.Println("Defer ...")
		res += 100
	}()
	return
}

func main() {
	res := demo2(1)
	fmt.Printf("get result: %d\n", res)
}

//运行结果
//Defer ...
//get result: 101

可见是否声明返回值对运行结果有很大的影响!

声明返回值时 defer函数改变了res值,未声明返回值时 defer函数无法改变res值

结论

  • 1.return 先给返回值赋值
  • 已经声明返回值则直接赋值
  • 未声明返回值则先声明一个匿名返回值再赋值
  • 2.调用RET返回指令并传入返回值
  • RET则会检查defer是否存在,若存在就先逆序插入defer语句
  • 3.RET携带返回值退出函数;

‍‍因此defer、return、返回值三者的执行顺序应该是:return给返回值赋值–>defer开始执行–>最后RET指令携带返回值退出函数

查看汇编代码来一探究竟

  • 将fmt.Println换成 println这个内建函数
  • 假设你的源文件名字是main.go
  • 对声明返回值与未声明返回值版本分别执行以下命令
GOOS=linux GOARCH=386 go tool compile -S main.go >> main.S

截取部分汇编代码

"".demo1 STEXT size=106 args=0x8 locals=0x10
	0x0000 00000 (defer_topic.go:15)	TEXT	"".demo1(SB), $16-8
	0x0000 00000 (defer_topic.go:15)	MOVL	TLS, CX
	0x0007 00007 (defer_topic.go:15)	MOVL	(CX)(TLS*2), CX
	0x000d 00013 (defer_topic.go:15)	CMPL	SP, 8(CX)
	0x0010 00016 (defer_topic.go:15)	JLS	99
	0x0012 00018 (defer_topic.go:15)	SUBL	$16, SP
	0x0015 00021 (defer_topic.go:15)	FUNCDATA	$0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x0015 00021 (defer_topic.go:15)	FUNCDATA	$1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x0015 00021 (defer_topic.go:15)	FUNCDATA	$3, gclocals·9fb7f0986f647f17cb53dda1484e0f7a(SB)
	0x0015 00021 (defer_topic.go:15)	PCDATA	$2, $0
	0x0015 00021 (defer_topic.go:15)	PCDATA	$0, $0
	0x0015 00021 (defer_topic.go:15)	MOVL	$0, "".~r1+24(SP)
	0x001d 00029 (defer_topic.go:16)	MOVL	"".a+20(SP), AX
	0x0021 00033 (defer_topic.go:16)	MOVL	AX, "".res+12(SP)
	0x0025 00037 (defer_topic.go:20)	PCDATA	$2, $1
	0x0025 00037 (defer_topic.go:20)	LEAL	"".res+12(SP), AX
	0x0029 00041 (defer_topic.go:20)	PCDATA	$2, $0
	0x0029 00041 (defer_topic.go:20)	MOVL	AX, 8(SP)
	0x002d 00045 (defer_topic.go:17)	MOVL	$4, (SP)
	0x0034 00052 (defer_topic.go:17)	PCDATA	$2, $1
	0x0034 00052 (defer_topic.go:17)	LEAL	"".demo1.func1·f(SB), AX
	0x003a 00058 (defer_topic.go:17)	PCDATA	$2, $0
	0x003a 00058 (defer_topic.go:17)	MOVL	AX, 4(SP)
	0x003e 00062 (defer_topic.go:17)	CALL	runtime.deferproc(SB)
	0x0043 00067 (defer_topic.go:17)	TESTL	AX, AX
	0x0045 00069 (defer_topic.go:17)	JNE	89
	0x0047 00071 (defer_topic.go:21)	MOVL	"".res+12(SP), AX
	0x004b 00075 (defer_topic.go:21)	MOVL	AX, "".~r1+24(SP)
	0x004f 00079 (defer_topic.go:21)	XCHGL	AX, AX
	0x0050 00080 (defer_topic.go:21)	CALL	runtime.deferreturn(SB)
	0x0055 00085 (defer_topic.go:21)	ADDL	$16, SP
	0x0058 00088 (defer_topic.go:21)	RET
	0x0059 00089 (defer_topic.go:17)	XCHGL	AX, AX
	0x005a 00090 (defer_topic.go:17)	CALL	runtime.deferreturn(SB)
	0x005f 00095 (defer_topic.go:17)	ADDL	$16, SP
	0x0062 00098 (defer_topic.go:17)	RET
"".demo2 STEXT size=98 args=0x8 locals=0xc
	0x0000 00000 (defer_topic.go:24)	TEXT	"".demo2(SB), $12-8
	0x0000 00000 (defer_topic.go:24)	MOVL	TLS, CX
	0x0007 00007 (defer_topic.go:24)	MOVL	(CX)(TLS*2), CX
	0x000d 00013 (defer_topic.go:24)	CMPL	SP, 8(CX)
	0x0010 00016 (defer_topic.go:24)	JLS	91
	0x0012 00018 (defer_topic.go:24)	SUBL	$12, SP
	0x0015 00021 (defer_topic.go:24)	FUNCDATA	$0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x0015 00021 (defer_topic.go:24)	FUNCDATA	$1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x0015 00021 (defer_topic.go:24)	FUNCDATA	$3, gclocals·9fb7f0986f647f17cb53dda1484e0f7a(SB)
	0x0015 00021 (defer_topic.go:24)	PCDATA	$2, $0
	0x0015 00021 (defer_topic.go:24)	PCDATA	$0, $0
	0x0015 00021 (defer_topic.go:24)	MOVL	$0, "".res+20(SP)
	0x001d 00029 (defer_topic.go:25)	MOVL	"".a+16(SP), AX
	0x0021 00033 (defer_topic.go:25)	MOVL	AX, "".res+20(SP)
	0x0025 00037 (defer_topic.go:29)	PCDATA	$2, $1
	0x0025 00037 (defer_topic.go:29)	LEAL	"".res+20(SP), AX
	0x0029 00041 (defer_topic.go:29)	PCDATA	$2, $0
	0x0029 00041 (defer_topic.go:29)	MOVL	AX, 8(SP)
	0x002d 00045 (defer_topic.go:26)	MOVL	$4, (SP)
	0x0034 00052 (defer_topic.go:26)	PCDATA	$2, $1
	0x0034 00052 (defer_topic.go:26)	LEAL	"".demo2.func1·f(SB), AX
	0x003a 00058 (defer_topic.go:26)	PCDATA	$2, $0
	0x003a 00058 (defer_topic.go:26)	MOVL	AX, 4(SP)
	0x003e 00062 (defer_topic.go:26)	CALL	runtime.deferproc(SB)
	0x0043 00067 (defer_topic.go:26)	TESTL	AX, AX
	0x0045 00069 (defer_topic.go:26)	JNE	81
	0x0047 00071 (defer_topic.go:30)	XCHGL	AX, AX
	0x0048 00072 (defer_topic.go:30)	CALL	runtime.deferreturn(SB)
	0x004d 00077 (defer_topic.go:30)	ADDL	$12, SP
	0x0050 00080 (defer_topic.go:30)	RET
	0x0051 00081 (defer_topic.go:26)	XCHGL	AX, AX
	0x0052 00082 (defer_topic.go:26)	CALL	runtime.deferreturn(SB)
	0x0057 00087 (defer_topic.go:26)	ADDL	$12, SP
	0x005a 00090 (defer_topic.go:26)	RET