个人主页:[https://polar9527.github.io]

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 语言那样写交换函数再内联了,相当于把脏活丢给编译器干了。

golang多重赋值的经典应用

leetcode 205 reverse linked list

/*
 * @lc app=leetcode id=206 lang=golang
 *
 * [206] Reverse Linked List
 */
/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func reverseList(head *ListNode) *ListNode {
	var tail *ListNode
	for head != nil {
		head.Next, tail, head = tail, head, head.Next
	}
	return tail
}

欢迎转载,请注明出处~ 作者个人主页