前一段时间有人询问a question Golang如何在a, b = b, a
等语句中实际交换变量。
要回答,我拿出了我的Golang编译器,戴上了我的思维上限并为所述问题制作了an answer。所以问题应该是自成一体的,所以这里的答案因简洁而被截断:
要弄清楚编译器如何生成本机代码,我们需要查看 它生成的汇编代码,由代码转换为机器代码 链接器。
我写了一个Go程序来帮助解决这个问题:
package main import "fmt" func main() { fmt.Println(myfunction()) } func myfunction() []int { a, b := 10, 5 b, a = a, b return []int{a, b} }
使用
)的输出会有所不同go tool compile -S > swap.s
,我找到了这四行,与第一行相对应 Go代码中有两行myfunction
:(注意这是我的64位代码 机;其他架构如32位(0x0028 00040 (swap.go:10) MOVQ $10, CX ; var a = 10 0x002f 00047 (swap.go:10) MOVQ $5, AX ; var b = 5 0x0036 00054 (swap.go:11) MOVQ CX, "".b+16(SP) ; copy a to *b+16 0x003b 00059 (swap.go:11) MOVQ AX, "".a+24(SP) ; copy b to *a+24
查看the Golang docs on asm,我们可以看到汇编程序使用间接来处理值。
当程序运行时,CPU足够聪明,可以看到发生了什么 并使用寄存器来避免覆盖现有值。
我的comment,根据我对(英特尔)x86大会的了解,得到了6票,我的回答得到了接受和3票。
Golang程序集实际执行的四行是什么?我的回答是否正确?
我问,因为我所链接的docs并非(根本没有)详尽无遗。
答案 0 :(得分:3)
a, b := 10, 5
b, a = a, b
0x0028 00040 (swap.go:10) MOVQ $10, CX ; CX = 10
0x002f 00047 (swap.go:10) MOVQ $5, AX ; AX = 5
0x0036 00054 (swap.go:11) MOVQ CX, "".b+16(SP) ; b = CX or *(SP+16) = CX
0x003b 00059 (swap.go:11) MOVQ AX, "".a+24(SP) ; a = AX or *(SP+24) = CX
CX
,AX
和SP
是寄存器。 a
和b
分别是SP + 24和SP + 16的堆栈变量。
答案 1 :(得分:3)
它将常量10和5加载到CPU寄存器中,然后将寄存器存储到为变量a
和b
保留的堆栈位置。
相当于:
CX := 10
AX := 5
b := CX
a := AX
注意,一个不错的优化编译器应该将其优化为将常量直接存储到堆栈中的位置的代码:
b := 10
a := 5
或者更好地完全消除变量:
return []int{5, 10}