Go 从「;“围棋之旅”为什么这个片会改变它的容量?

Go 从「;“围棋之旅”为什么这个片会改变它的容量?,go,Go,我是刚来戈朗的。有以下片段: package main import "fmt" func main() { s := []int{2, 3, 5, 7, 11, 13} printSlice(s) // Slice the slice to give it zero length. s = s[:0] printSlice(s) // Extend its length. s = s[:4] printSlice(s)

我是刚来戈朗的。有以下片段:

package main

import "fmt"

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

    // Slice the slice to give it zero length.
    s = s[:0]
    printSlice(s)

    // Extend its length.
    s = s[:4]
    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=0 cap=6 []
len=4 cap=6 [2 3 5 7]
len=2 cap=4 [5 7]

我的困惑是最后一行中的
cap=4
——我认为应该是6。最后一行容量从6变为4的原因是什么?另外,为什么只有最后一行更改其容量,而其他行则不更改?

请记住,切片在数组中保存数据。通过删除前两个元素,我们将切片的开头向右移动,现在在数组中切片的开头和数组的结尾之间的插槽更少

切片末端的拖放元素对容量没有影响,因为阵列内部切片起点和背衬阵列末端之间的距离没有改变

这两个操作都不会修改备份数组,它们只是修改切片数据

参见第节“切片内部构件”中解释的观察到的行为

通过打印切片标题,您可以看到正在发生的更改

func printSlice(s []int) {
    sh := (*reflect.SliceHeader)(unsafe.Pointer(&s))
    fmt.Printf("header=%+v len=%d cap=%d %v\n", sh, len(s), cap(s), s)
}
在最后一次调用中,数据指针向前移动

header=&{Data:272990208 Len:6 Cap:6} len=6 cap=6 [2 3 5 7 11 13]
header=&{Data:272990208 Len:0 Cap:6} len=0 cap=6 []
header=&{Data:272990208 Len:4 Cap:6} len=4 cap=6 [2 3 5 7]
header=&{Data:272990216 Len:2 Cap:4} len=2 cap=4 [5 7]

基本上,一个片有3个元素:指向数据的指针、长度和容量。指向数据的指针是对底层数组(固定大小、连续分配)的引用

可以将此切片的任何子集形成另一个切片。如果从大小为N(m0)个元素,则新切片中有项目0到m,同时整个底层阵列容量可用(因为指向数据的指针仍然指向位置0)。所以我们可以把它扩展到N,而不改变容量


然而,如果一个切片m(m>0)到k(k这说明了这一点,数组由实际值组成,相反,切片包含指向数组的指针,更具体地说,指针指向数组的元素:“容量是基础数组中的元素数(从切片指针所指的元素开始)

我认为问题在于为什么删除最后两个元素的效果不一样。谢谢。那么,这是否意味着删除最后一个元素和第一个元素是非常不同的操作?删除最后一个元素实际上只是创建一个“视图”但是删除第一个元素实际上修改了底层数组,并根据它为我们提供了一个视图?@akai确实,它们完全不同。我已经检查了您的编辑并将阅读博客文章,再次感谢。值得注意的是,所有切片操作都只是“视图”,正如您所说。删除第一个元素不会像删除最后一个元素那样修改底层数组。它所做的是将切片标头中的“开始”指针向前推进。切片的“长度”和“容量”值从该“开始”指针引用(这就是上面代码中的
数据
).如果您有一个长度为6的切片,并且您将开始指针向前移动了2个元素,那么现在距离开始只有4个元素,因此容量将减少到4。