内部结构
切片表面上用起来像是一个可变数组,但它其实是一个结构体,内部结构如下:

切片分为三个参数
- 指针,指向一片连续的存储空间,也就是数组
- len(长度),我们可以访问的数组元素个数
- cap(容量),指针指向的数组的储存空间的长度
len和cap到底具体是什么意思,有什么区别?
我们可以通过代码来打印下长度和容量来观察
var s0 []int
t.Log(len(s0), cap(s0)) //打印结果0 0
s0 = append(s0, 1)
t.Log(len(s0), cap(s0)) //打印结果1 1
一开始初始化,没有元素,所以len和cap都是0。append一个元素1后,len和cap都增长成了1。
s1 := []int{1, 2, 3, 4}
t.Log(len(s1), cap(s1)) //打印结果4 4
len和cap都是4。
s2 := make([]int, 3, 5)
t.Log(len(s2), cap(s2)) //打印结果3 5
t.Log(s2[0], s2[1], s2[2])
t.Log(s2[0], s2[1], s2[2], s2[3]) //报错,len仅为3,因此访问第4个元素报错
s2 = append(s2, 1)
t.Log(s2[0], s2[1], s2[2], s2[3])
t.Log(len(s2), cap(s2)) //打印结果4 5
在申明切片的时候用make,指定len3和cap5,因此打印出来len为3、cap为5。此时注意我输出了切片各个元素,输出3个元素的时候正产输出,但第4个的时候报错了,可见len代表的就是我们能访问的数组个数。append后,len增加了1,但cap因为len并未大于cap,因此cap仍为5。
切片如何实现可变长
先看一段代码和输出就明白了
s := []int{}
for i := 0; i < 10; i++ {
s = append(s, i)
t.Log(len(s), cap(s))
}

可以发现随着长度len的增长,容量cap也在增长。随着len要超过cap的时候cap会翻倍增长。
PS:看到这里也理解为什么切片增加元素的代码是s = append(s, i)这样写。按照感觉应该append(s, i)就可以,为什么前面还需要重新赋值。因为我们也看到了随着cap的增长,他的存储空间地址发生了变化,并不是在原有存储空间里增加元素。
值得注意的一点是,slice切片的内存自增长的代价,涉及到性能调优。
切⽚共享存储结构

如何看待此图呢,所谓共享存储,比如图中Q2和summer切片,因为共享months切片的存储空间,修改任一方的元素,都会对另外一方造成影响。
还有一点需要注意:Q2截取了months切片中的下标4到7的元素,长度len为3,但cap是9;summer截取了months切片中的下标6到9的元素,长度len为3,但cap是7。
year := []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep","Oct", "Nov", "Dec"}
Q2 := year[3:6]
t.Log(Q2, len(Q2), cap(Q2))
summer := year[5:8]
t.Log(summer, len(summer), cap(summer))
summer[0] = "Unknow"
t.Log(Q2)
t.Log(year)

看打印结果可以发现,修改了summer中的一个元素后,无论是Q2还是months中的元素都发生了改变。
网友评论