Go问答 群里看到的一道题,大家帮忙分析分析。

haoc7 · 2018年04月28日 · 最后由 hej8875 回复于 2018年05月02日 · 580 次阅读
package main

import (
    "fmt"
)

func main() {
    s := []byte("")
    s1 := append(s, 'a')

    s2 := append(s, 'b')

    fmt.Print(string(s1), string(s2))

}

输出是
ab
aa
bb
abab

用 dlv 调试下:


Type 'help' for list of commands.
(dlv) break main.main
Breakpoint 1 set at 0x49c22b for main.main() ./t1.go:7
(dlv) continue
> main.main() ./t1.go:7 (hits goroutine(1):1 total:1) (PC: 0x49c22b)
     2:
     3: import (
     4:         "fmt"
     5: )
     6:
=>   7: func main() {
     8:         s := []byte("")
     9:         s1 := append(s, 'a')
    10:
    11:         s2 := append(s, 'b')
    12:
(dlv) print s
Command failed: could not find symbol value for s
(dlv) next
> main.main() ./t1.go:8 (PC: 0x49c242)
     3: import (
     4:         "fmt"
     5: )
     6:
     7: func main() {
=>   8:         s := []byte("")
     9:         s1 := append(s, 'a')
    10:
    11:         s2 := append(s, 'b')
    12:
    13:         fmt.Print(string(s1), string(s2))
(dlv) print s
[]uint8 len: 0, cap: 842350772072, []
(dlv) next
> main.main() ./t1.go:9 (PC: 0x49c27f)
     4:         "fmt"
     5: )
     6:
     7: func main() {
     8:         s := []byte("")
=>   9:         s1 := append(s, 'a')
    10:
    11:         s2 := append(s, 'b')
    12:
    13:         fmt.Print(string(s1), string(s2))
    14:
(dlv) print s
[]uint8 len: 0, cap: 32, []
(dlv) next
> main.main() ./t1.go:11 (PC: 0x49c2ac)
     6:
     7: func main() {
     8:         s := []byte("")
     9:         s1 := append(s, 'a')
    10:
=>  11:         s2 := append(s, 'b')
    12:
    13:         fmt.Print(string(s1), string(s2))
    14:
    15: }
(dlv) print s
[]uint8 len: 0, cap: 32, []
(dlv) print s1
[]uint8 len: 1, cap: 32, [97]
(dlv) next
> main.main() ./t1.go:13 (PC: 0x49c2f1)
     8:         s := []byte("")
     9:         s1 := append(s, 'a')
    10:
    11:         s2 := append(s, 'b')
    12:
=>  13:         fmt.Print(string(s1), string(s2))
    14:
    15: }
(dlv) print s
[]uint8 len: 0, cap: 32, []
(dlv) print s1
[]uint8 len: 1, cap: 32, [98]
(dlv) print s2
[]uint8 len: 1, cap: 32, [98]
(dlv)

最后的输出结果出乎为什么是 bb 呢?

更多原创文章干货分享,请关注公众号
  • 加微信实战群请加微信(注明:实战群):gocnio

因为 [] byte("") 的 capacity 是 32,所以 append 判断能装下的情况不会重新分配,导致第二次 append 会覆盖第一次的结果,且 s1 和 s2 指向同一个地址:

package main

import (
    "fmt"
)

func main() {
    s := []byte("")
    fmt.Println(len(s), cap(s))
}

这道题不严谨,因为 [] byte("") 的 cap 到底应该是多少,属于未定义行为,允许编译器根据上下文做优化和调整。所以不用纠结题目本身。

如果我们显式指定 s 的 cap,需要关注的是 append 得到的 slice 底层对应的地址,以及每个 slice 的 len 和 cap:

package main

import (
    "fmt"
)

func main() {
    s := make([]byte, 0, 32)
    fmt.Println(len(s), cap(s))

    s1 := append(s, 'a', 'c')
    s2 := append(s, 'b')

    fmt.Printf("len=%d, cap=%d, addr=%p, val=%s\n", len(s1), cap(s1), &s1[0], string(s1))
    fmt.Printf("len=%d, cap=%d, addr=%p, val=%s\n", len(s2), cap(s2), &s2[0], string(s2))
}

这是一个烂题

这个是考察 slice 内部实现原理, 其中的关键点也确实是一个容易踩坑的地方, 导致的 bug 也比较隐蔽. 另外, 答案也可能是 aa.

https://sheepbao.github.io/post/understand_golang_slice/ 可以看看我这篇博客,s1 和 s2 共用一个内存地址,而导致 s2 更改了 s1 的内容。

package main

import "fmt"

func main() {
s := []byte("")
s1 := append(s, 'a')
s2 := append(s, 'b')

//fmt.Println(s1, "==========", s2)
fmt.Println(string(s1), "==========", string(s2))
}


出现个让我理解不了的现象, 注释时候输出是 b ========== b 取消注释输出是 [97] ========== [98] a ========== b

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册