《the way to go》一處關於go匿名函數的「勘誤」

《the way to go》是go的一門經典入門書籍,可是因爲書的編纂比較早,有些知識點已通過時了。架構

在6.8章節中有這麼一處代碼,函數

package main

import "fmt"

func main() {
	f()
}
func f() {
	for i := 0; i < 4; i++ {
		g := func(i int) { fmt.Printf("%d", i) }
		g(i)
		fmt.Printf(" - g is of type %T and has value %v\n", g, g)
	}
}

輸出測試

0 - g is of type func(int) and has value 0x681a80 
1 - g is of type func(int) and has value 0x681b00 
2 - g is of type func(int) and has value 0x681ac0 
3 - g is of type func(int) and has value 0x681400

可是,當你本身去運行這個代碼的時候,你就發現,輸出四次的函數內存地址是同樣的,這裏咱們寫一個簡易的代碼,而後把它轉成彙編,看一下優化

package main

func main() {
	for i := 0; i < 4; i++ {
		g := func() {}
		_ = g
	}
}

執行命令rest

go tool compile -S main.go

獲得彙編代碼code

"".main t=1 size=18 args=0x0 locals=0x0
	0x0000 00000 (main.go:3)	TEXT	"".main(SB), $0-0
	0x0000 00000 (main.go:3)	FUNCDATA	$0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x0000 00000 (main.go:3)	FUNCDATA	$1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x0000 00000 (main.go:4)	MOVQ	$0, AX
	0x0002 00002 (main.go:4)	CMPQ	AX, $4
	0x0006 00006 (main.go:4)	JGE	$0, 17
	0x0008 00008 (main.go:4)	INCQ	AX
	0x000b 00011 (main.go:4)	CMPQ	AX, $4
	0x000f 00015 (main.go:4)	JLT	$0, 8
	0x0011 00017 (main.go:8)	RET
	0x0000 31 c0 48 83 f8 04 7d 09 48 ff c0 48 83 f8 04 7c  1.H...}.H..H...|
	0x0010 f7 c3                                            ..
"".main.func1 t=1 size=1 args=0x0 locals=0x0
	0x0000 00000 (main.go:5)	TEXT	"".main.func1(SB), $0-0
	0x0000 00000 (main.go:5)	FUNCDATA	$0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x0000 00000 (main.go:5)	FUNCDATA	$1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x0000 00000 (main.go:5)	RET
	0x0000 c3                                               .
"".init t=1 size=79 args=0x0 locals=0x8
	0x0000 00000 (main.go:9)	TEXT	"".init(SB), $8-0
	0x0000 00000 (main.go:9)	MOVQ	(TLS), CX
	0x0009 00009 (main.go:9)	CMPQ	SP, 16(CX)
	0x000d 00013 (main.go:9)	JLS	72
	0x000f 00015 (main.go:9)	SUBQ	$8, SP
	0x0013 00019 (main.go:9)	MOVQ	BP, (SP)
	0x0017 00023 (main.go:9)	LEAQ	(SP), BP
	0x001b 00027 (main.go:9)	FUNCDATA	$0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x001b 00027 (main.go:9)	FUNCDATA	$1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x001b 00027 (main.go:9)	MOVBLZX	"".initdone·(SB), AX
	0x0022 00034 (main.go:9)	CMPB	AL, $1
	0x0024 00036 (main.go:9)	JLS	$0, 47
	0x0026 00038 (main.go:9)	MOVQ	(SP), BP
	0x002a 00042 (main.go:9)	ADDQ	$8, SP
	0x002e 00046 (main.go:9)	RET
	0x002f 00047 (main.go:9)	JNE	$0, 56
	0x0031 00049 (main.go:9)	PCDATA	$0, $0
	0x0031 00049 (main.go:9)	CALL	runtime.throwinit(SB)
	0x0036 00054 (main.go:9)	UNDEF
	0x0038 00056 (main.go:9)	MOVB	$2, "".initdone·(SB)
	0x003f 00063 (main.go:9)	MOVQ	(SP), BP
	0x0043 00067 (main.go:9)	ADDQ	$8, SP
	0x0047 00071 (main.go:9)	RET
	0x0048 00072 (main.go:9)	NOP
	0x0048 00072 (main.go:9)	PCDATA	$0, $-1
	0x0048 00072 (main.go:9)	CALL	runtime.morestack_noctxt(SB)
	0x004d 00077 (main.go:9)	JMP	0
	0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 76 39 48  eH..%....H;a.v9H
	0x0010 83 ec 08 48 89 2c 24 48 8d 2c 24 0f b6 05 00 00  ...H.,$H.,$.....
	0x0020 00 00 3c 01 76 09 48 8b 2c 24 48 83 c4 08 c3 75  ..<.v.H.,$H....u
	0x0030 07 e8 00 00 00 00 0f 0b c6 05 00 00 00 00 02 48  ...............H
	0x0040 8b 2c 24 48 83 c4 08 c3 e8 00 00 00 00 eb b1     .,$H...........
	rel 5+4 t=16 TLS+0
	rel 30+4 t=15 "".initdone·+0
	rel 50+4 t=8 runtime.throwinit+0
	rel 58+4 t=15 "".initdone·+-1
	rel 73+4 t=8 runtime.morestack_noctxt+0
gclocals·33cdeccccebe80329f1fdbee7f5874cb t=8 dupok size=8
	0x0000 01 00 00 00 00 00 00 00                          ........
go.info."".main t=45 size=27
	0x0000 02 22 22 2e 6d 61 69 6e 00 00 00 00 00 00 00 00  ."".main........
	0x0010 00 00 00 00 00 00 00 00 00 01 00                 ...........
	rel 9+8 t=1 "".main+0
	rel 17+8 t=1 "".main+18
go.info."".main.func1 t=45 size=33
	0x0000 02 22 22 2e 6d 61 69 6e 2e 66 75 6e 63 31 00 00  ."".main.func1..
	0x0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01  ................
	0x0020 00                                               .
	rel 15+8 t=1 "".main.func1+0
	rel 23+8 t=1 "".main.func1+1
go.info."".init t=45 size=27
	0x0000 02 22 22 2e 69 6e 69 74 00 00 00 00 00 00 00 00  ."".init........
	0x0010 00 00 00 00 00 00 00 00 00 01 00                 ...........
	rel 9+8 t=1 "".init+0
	rel 17+8 t=1 "".init+79
"".initdone· t=32 size=1
"".main.func1·f t=8 dupok size=8
	0x0000 00 00 00 00 00 00 00 00                          ........
	rel 0+8 t=1 "".main.func1+0

請注意這裏內存

"".main.func1 t=1 size=1 args=0x0 locals=0x0
	0x0000 00000 (main.go:5)	TEXT	"".main.func1(SB), $0-0
	0x0000 00000 (main.go:5)	FUNCDATA	$0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x0000 00000 (main.go:5)	FUNCDATA	$1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x0000 00000 (main.go:5)	RET
	0x0000 c3

這裏就是生成匿名函數的彙編代碼,能夠看到,整個代碼只生成一次匿名函數。編譯器

我測試了1.2-1.8版本,都是同樣的。it

我的推測,《the way to go》編寫的2012的時候,go1.0剛剛發佈,編譯器並無對這種代碼作優化,因此出現了書中描述的現象,編譯

更多架構、PHP、GO相關踩坑實踐技巧請關注個人公衆號:PHP架構師

相關文章
相關標籤/搜索