go defer
Tip
常用于关闭资源,先进后执行
1 例子分析
1.1 例子1
1.2 例子2
| OS | go version |
|---|---|
| Ubuntu 20.04.3 LTS | go1.20.4 |
汇编代码使用的是go 1.15.4生成的, 省略部分.
"".defer1 STEXT size=155 args=0x8 locals=0x68
0x0000 00000 (main.go:3) TEXT "".defer1(SB), ABIInternal, $104-8
0x0013 00019 (main.go:3) SUBQ $104, SP
0x0017 00023 (main.go:3) MOVQ BP, 96(SP)
0x001c 00028 (main.go:3) LEAQ 96(SP), BP
# 返回值 r0
0x0021 00033 (main.go:3) MOVQ $0, "".~r0+112(SP)
# i
0x002a 00042 (main.go:4) MOVQ $2, "".i+8(SP)
0x0033 00051 (main.go:5) MOVL $8, ""..autotmp_3+16(SP)
0x003b 00059 (main.go:5) LEAQ "".defer1.func1·f(SB), AX
0x0042 00066 (main.go:5) MOVQ AX, ""..autotmp_3+40(SP)
0x0047 00071 (main.go:5) LEAQ "".i+8(SP), AX
0x004c 00076 (main.go:5) MOVQ AX, ""..autotmp_3+88(SP)
0x0051 00081 (main.go:5) LEAQ ""..autotmp_3+16(SP), AX
0x0056 00086 (main.go:5) MOVQ AX, (SP)
0x005a 00090 (main.go:5) CALL runtime.deferprocStack(SB)
0x005f 00095 (main.go:5) NOP
0x0060 00096 (main.go:5) TESTL AX, AX
0x0062 00098 (main.go:5) JNE 129
0x0064 00100 (main.go:5) JMP 102
0x0066 00102 (main.go:9) MOVQ "".i+8(SP), AX
# i 附给 返回值
0x006b 00107 (main.go:9) MOVQ AX, "".~r0+112(SP)
0x0070 00112 (main.go:9) XCHGL AX, AX
0x0071 00113 (main.go:9) CALL runtime.deferreturn(SB)
"".defer1.func1 STEXT nosplit size=13 args=0x8 locals=0x0
0x0000 00000 (main.go:5) TEXT "".defer1.func1(SB), NOSPLIT|ABIInternal, $0-8
# i的地址 给AX
0x0000 00000 (main.go:6) MOVQ "".&i+8(SP), AX
# 赋值3 给 i
0x0005 00005 (main.go:6) MOVQ $3, (AX)
"".main STEXT size=98 args=0x0 locals=0x18
0x0000 00000 (main.go:11) TEXT "".main(SB), ABIInternal, $24-0
0x000f 00015 (main.go:11) SUBQ $24, SP
0x0013 00019 (main.go:11) MOVQ BP, 16(SP)
0x0018 00024 (main.go:11) LEAQ 16(SP), BP
0x0020 00032 (main.go:12) CALL "".defer1(SB)
# (SP) 就是 r0
0x0025 00037 (main.go:12) MOVQ (SP), AX
# 赋值给 ret
0x0029 00041 (main.go:12) MOVQ AX, "".ret+8(SP)
0x002e 00046 (main.go:13) CALL runtime.printlock(SB)
0x0033 00051 (main.go:13) MOVQ "".ret+8(SP), AX
0x0038 00056 (main.go:13) MOVQ AX, (SP)"".defer1 STEXT size=140 args=0x8 locals=0x60
0x0000 00000 (main.go:3) TEXT "".defer1(SB), ABIInternal, $96-8
0x000f 00015 (main.go:3) SUBQ $96, SP
0x0013 00019 (main.go:3) MOVQ BP, 88(SP)
0x0018 00024 (main.go:3) LEAQ 88(SP), BP
# i 赋值
0x001d 00029 (main.go:3) MOVQ $0, "".i+104(SP)
0x0026 00038 (main.go:4) MOVQ $2, "".i+104(SP)
0x002f 00047 (main.go:5) MOVL $8, ""..autotmp_2+8(SP)
0x0037 00055 (main.go:5) LEAQ "".defer1.func1·f(SB), AX
0x003e 00062 (main.go:5) MOVQ AX, ""..autotmp_2+32(SP)
0x0043 00067 (main.go:5) LEAQ "".i+104(SP), AX
0x0048 00072 (main.go:5) MOVQ AX, ""..autotmp_2+80(SP)
0x004d 00077 (main.go:5) LEAQ ""..autotmp_2+8(SP), AX
0x0052 00082 (main.go:5) MOVQ AX, (SP)
0x0056 00086 (main.go:5) PCDATA $1, $0
0x0056 00086 (main.go:5) CALL runtime.deferprocStack(SB)
0x005b 00091 (main.go:5) TESTL AX, AX
0x005d 00093 (main.go:5) JNE 114
0x005f 00095 (main.go:5) NOP
0x0060 00096 (main.go:5) JMP 98
0x0062 00098 (main.go:9) XCHGL AX, AX
0x0063 00099 (main.go:9) CALL runtime.deferreturn(SB)
"".defer1.func1 STEXT nosplit size=13 args=0x8 locals=0x0
0x0000 00000 (main.go:5) TEXT "".defer1.func1(SB), NOSPLIT|ABIInternal, $0-8
# i的地址 给AX
0x0000 00000 (main.go:6) MOVQ "".&i+8(SP), AX
# 赋值3 给 i
0x0005 00005 (main.go:6) MOVQ $3, (AX)
"".main STEXT size=98 args=0x0 locals=0x18
0x0000 00000 (main.go:11) TEXT "".main(SB), ABIInternal, $24-0
0x000f 00015 (main.go:11) SUBQ $24, SP
0x0013 00019 (main.go:11) MOVQ BP, 16(SP)
0x0018 00024 (main.go:11) LEAQ 16(SP), BP
0x0020 00032 (main.go:12) CALL "".defer1(SB)
# (SP) 就是 返回值也是 i
0x0025 00037 (main.go:12) MOVQ (SP), AX
# 赋值给 ret
0x0029 00041 (main.go:12) MOVQ AX, "".ret+8(SP)
0x002e 00046 (main.go:13) CALL runtime.printlock(SB)
0x0033 00051 (main.go:13) MOVQ "".ret+8(SP), AX
0x0038 00056 (main.go:13) MOVQ AX, (SP)先设置返回值,然后执行defer ,最后将返回值复制给ret
有名返回值的情况是 返回值与i变量是同一个内存位置, defer会修改这个i,就是修改了返回值,最后复制给ret,这样结果就是3了
匿名返回值的情况是 会有一个临时变量(一块在main栈帧中的内存作为返回值),先设置返回值=2,接着执行defer,修改的是defer1函数栈帧中的 i变量,对返回值无影响