关于append:Go中切片的容量

Capacity of slices in Go

我有一个关于下面代码的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import"fmt"

func main() {
    var a []int
    printSlice("a", a)

    // append works on nil slices.
    a = append(a, 0)
    printSlice("a", a)

    // the slice grows as needed.
    a = append(a, 1)
    printSlice("a", a)

    // we can add more than one element at a time.
    a = append(a, 2, 3, 4)
    printSlice("a", a)
}

func printSlice(s string, x []int) {
    fmt.Printf("%s len=%d cap=%d %v
",
    s, len(x), cap(x), x)
}

我总是猜测运行一段代码的结果会是什么样子,然后运行代码并检查我的猜测是否正确。但这段代码与我的猜测有点不同:

结果:在本地Go Tour服务器上:

1
2
3
4
a len=0 cap=0 []
a len=1 cap=1 [0]
a len=2 cap=2 [0 1]
a len=5 cap=6 [0 1 2 3 4]

到最后一行一切都好,但我不知道

1
cap=6

为什么不

1
cap=5

我的观点是,我没有用显式的能力创建slice,因此我的系统给它6的值。

2)但是当我在Golang Tour服务器上尝试相同的代码时,我得到了一个更为不同的结果,比如:

1
2
3
4
a len=0 cap=0 []
a len=1 cap=2 [0]
a len=2 cap=2 [0 1]
a len=5 cap=8 [0 1 2 3 4]

第二行的cap=2,最后一行的cap=8怎么样?


这个问题不完全重复,但我在这里的回答也有效地回答了这个问题。

tl;dr-规范中没有提到片的扩展容量,go的不同版本(或不同的实现,或不同体系结构上的相同版本等)可以将片扩展不同的数量。

您可能希望使容量大于所需容量的原因是,在切片下面有一个不可变的数组(它不能扩展)。当您"增长"一个切片时,实际发生的是您创建一个新的(更长的)数组,复制所有值,然后将其设置为切片的后备数组。如果您要附加大量的值,则必须进行大量的复制(每个值一个),这将非常缓慢,因此运行时分配的空间比您认为需要的要大,因此它必须减少复制的频率。