理解Golang多重賦值

polar9527發表於2019-05-16

個人主頁 Golang 的多重賦值可以用於值交換操作

兩個值可以方便的如下交換:

a, b = b, a

四個值同樣可以如下交換

a, b, c, d = b, c, a, d

那這個交換底層是怎麼實現的呢? 我們來做個試驗,看看這一段四值交換 golang 程式碼的的彙編程式碼

func main(){
    a := 1
    b := 2
    c := 3
    d := 4
    a, b, c, d = b, c, a, d
}
$>dlv debug main.go
Type 'help' for list of commands.
(dlv) b main.main
Breakpoint 1 set at 0x454b6a for main.main() ~/pathToProject/main.go:3
(dlv) r
Process restarted with PID 26528
(dlv) c
> main.main() ~/pathToProject/main.go:3 (hits goroutine(1):1 total:1) (PC: 0x454b6a)
     1: package main
     2:
=>   3: func main() {
     4:         a := 1
     5:         b := 2
     6:         c := 3
     7:         d := 4
(dlv) disassemble
TEXT main.main(SB) D:/Users/polar/go/src/mylab/main.go
        main.go:3       0x454b50        65488b0c2528000000      mov rcx, qword ptr gs:[0x28]
        main.go:3       0x454b59        488b8900000000          mov rcx, qword ptr [rcx]
        main.go:3       0x454b60        483b6110                cmp rsp, qword ptr [rcx+0x10]
        main.go:3       0x454b64        0f8619020000            jbe 0x454d83
=>      main.go:3       0x454b6a*       4883ec50                sub rsp, 0x50
        main.go:3       0x454b6e        48896c2448              mov qword ptr [rsp+0x48], rbp
        main.go:3       0x454b73        488d6c2448              lea rbp, ptr [rsp+0x48]
        main.go:4       0x454b78        48c744242801000000      mov qword ptr [rsp+0x28], 0x1  // a := 1
        main.go:5       0x454b81        48c744242002000000      mov qword ptr [rsp+0x20], 0x2  // b := 2
        main.go:6       0x454b8a        48c744241803000000      mov qword ptr [rsp+0x18], 0x3  // c := 3
        main.go:7       0x454b93        48c744241004000000      mov qword ptr [rsp+0x10], 0x4  // d := 4
        main.go:9       0x454b9c        488b442428              mov rax, qword ptr [rsp+0x28]
        main.go:9       0x454ba1        4889442440              mov qword ptr [rsp+0x40], rax  // temp = a
        main.go:9       0x454ba6        488b442420              mov rax, qword ptr [rsp+0x20]
        main.go:9       0x454bab        4889442428              mov qword ptr [rsp+0x28], rax  // a = b
        main.go:9       0x454bb0        488b442418              mov rax, qword ptr [rsp+0x18]
        main.go:9       0x454bb5        4889442420              mov qword ptr [rsp+0x20], rax  // b = c
        main.go:9       0x454bba        488b442410              mov rax, qword ptr [rsp+0x10]
        main.go:9       0x454bbf        4889442418              mov qword ptr [rsp+0x18], rax  // c = d
        main.go:9       0x454bc4        488b442440              mov rax, qword ptr [rsp+0x40]
        main.go:9       0x454bc9        4889442410              mov qword ptr [rsp+0x10], rax  // d = temp

很好理解了,就是編譯器幫我們在棧上建立了一個臨時變數 temp, 然後按順序交換其他各個變數的值。

那麼下面這種情況,會發生什麼呢?

a := 1
b := 2
a, b, a = b, a, b

a 和 b 最終的值是多少?

看一下彙編程式碼就清楚了

main.go:5   0x454b9b 48c744241801000000   mov qword ptr [rsp+0x18], 0x1  // a:=1
main.go:6   0x454ba4 48c744241002000000   mov qword ptr [rsp+0x10], 0x2  // b:=2
main.go:7   0x454bad 488b442418           mov rax, qword ptr [rsp+0x18]
main.go:7   0x454bb2 4889442428           mov qword ptr [rsp+0x28], rax  // aTemp := a
main.go:7   0x454bb7 488b442410           mov rax, qword ptr [rsp+0x10]
main.go:7   0x454bbc 4889442420           mov qword ptr [rsp+0x20], rax  // bTemp := b
main.go:7   0x454bc1 488b442410           mov rax, qword ptr [rsp+0x10]
main.go:7   0x454bc6 4889442418           mov qword ptr [rsp+0x18], rax  // a = b
main.go:7   0x454bcb 488b442428           mov rax, qword ptr [rsp+0x28]
main.go:7   0x454bd0 4889442410           mov qword ptr [rsp+0x10], rax  // b = aTemp
main.go:7   0x454bd5 488b442420           mov rax, qword ptr [rsp+0x20]
main.go:7   0x454bda 4889442418           mov qword ptr [rsp+0x18], rax  // a = bTemp

相當於

aTemp = a
bTemp = b
a, b, a = bTemp, aTemp, bTemp

這裡兩個值交換的操作的原理是將兩個被賦值的變數的值,都儲存在臨時變數裡,然後再用臨時變數去賦值。所以這個例子賦值順序對結果是無影響的,其結果仍然是 a = 2, b = 1。

不用再像 C 語言那樣寫交換函式再內聯了,相當於把髒活丟給編譯器幹了。

歡迎轉載,請註明出處~ 作者個人主頁

相關文章