为什么在删除前n个项目而不是最后n个项目时切片的容量会发生变化?

时间:2019-03-20 00:58:59

标签: go

我正在浏览Go,想知道以下内容:

package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}
    printSlice(s)

    // Drop its last two values
    s = s[:len(s)-2]
    printSlice(s)

    // Drop its first two values.
    s = s[2:]
    printSlice(s)
}

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

结果:

len=6 cap=6 [2 3 5 7 11 13]
len=4 cap=6 [2 3 5 7]
len=2 cap=4 [5 7]

为什么在丢弃最后2个项目时切片的容量保持不变,而在丢弃前2个项目时却改变了?

https://play.golang.org/p/ZNKwOYKDqOi

2 个答案:

答案 0 :(得分:5)

Go slice被实现为一个结构:

src/runtime/slice.go

type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

修改您的printSlice函数以显示指向基础数组的指针:

package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}
    printSlice(s)

    // Drop its last two values
    s = s[:len(s)-2]
    printSlice(s)

    // Drop its first two values.
    s = s[2:]
    printSlice(s)
}

func printSlice(s []int) {
    var ptr *int
    if cap(s) >= 1 {
        ptr = &s[:cap(s)][0]
    }
    fmt.Printf("ptr=%p len=%d cap=%d %v\n", ptr, len(s), cap(s), s)
}

游乐场:https://play.golang.org/p/pk3cpE_LsUV

输出:

ptr=0x450000 len=6 cap=6 [2 3 5 7 11 13]
ptr=0x450000 len=4 cap=6 [2 3 5 7]
ptr=0x450008 len=2 cap=4 [5 7]

查看切片操作如何调整指针,长度和容量。切片只是基础数组的视图或窗口。


参考文献:

The Go Blog: Go Slices: usage and internals

The Go Programming Language Specification:

答案 1 :(得分:0)

切片在封面下有一个缓冲区。容量是该缓冲区的大小。从末尾删除项目不会更改缓冲区大小。

您正在从现有切片中创建一个新切片,但是从2开始新切片的索引。新切片仍指向相同的基础缓冲区(但偏移量为2)。因此,此新切片的缓冲区大小(和容量)要少2。

当您看到追加的工作原理时,容量才有意义。 Append尝试重用相同的基础缓冲区-但如果容量已满,则会执行重新分配和复制。