了解了 slice 的结构后,你就明白了。先来看看 slice 在源码中的结构,如下:
// runtime/slice.go
type slice struct {
array unsafe.Pointer // 数组指针
len int // 长度
cap int // 容量
}
slice,即切片,它主要包含三个信息,切片的开始位置(数组的地址),切片大小以及底层数组容量。
正因为切片的底层结构是数组,所以就可以直接基于数组创建切片,比如:
a := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s := a[1:6]
此时的切片结构是这样的,图示如下:
访问切片 s[0] 其实就是访问的 a[1] 位置,其实可以改变一下 s[0] 的值,就会发现 a[1] 的值也跟着改变了。
s[0] = 10
fmt.Println(a[1])
输出的结果是 10。
切片的长度和底层结构的容量分别可以通过 len 和 cap 获得。
fmt.Println(len(s)) // 6
fmt.Println(cap(s)) // 8
如果底层结构的数组容量足够,改变 slice 的 len 即可,slice 地址不会该干。
如果底层结构的数组容量不够,Go 会自动帮助创建新的数组,并将 s 指向新创建的数组,但是同样,slice 本身的地址并没有改变,只是改变了地址中的内容。
扩容基本是按需要的 2 倍容量申请,可以通过如下代码测试下,不断进行翻倍追加。
for i := 0; i < 10; i++ {
fmt.Println(cap(s))
s = append(s, s...)
}
终于完成连续 90 天答题成就,以后可以认真写回答了,不用再担心每天必须答一个问题了。