切片和数组的异同?
-
切片的底层是数组,是对数组的封装。
-
数组是定长的,不同长度的数组表示不同类型,长度不可变
-
切片可以动态扩容。长度与类型无关
-
数组就是一片连续的内存, slice 实际上是一个结构体,包含三个字段:长度、容量、底层数组。
- 底层数组可以同时被多个切片所指向,所以更改一个切片的时候,有可能同时更改其他的切片
- 注意切片扩容是扩容为原来的二倍,扩容完全的切片和原来的切片没有关系了,再去做什么改变也不会影响之前的切片了
切片容量是怎么增长的?
- 通过append函数,实际上是底层数组添加元素。如果数组满了,就没法添加了。此时切片会迁移到新的内存位置,新的底层数组的长度增加。那么新的切片容量是多大呢?
我用的1.16.2 在1.18之前
扩容源码:
输入的cap实际上是append之后的len,所需要的容量。
if cap>原先容量的两倍,那么newcap = cap
else 表示当前所需容量(cap)不大于原容量的两倍(doublecap),则进行如下判断;
if 原来切片容量小于1024,则newcap = 2*原容量
else 原来切片容量大于1024,那么newcap += newcap/4,直到大于c所需cap在停下来。
得到新的newcap之后做一个内存对齐,传入roundupsize函数。代码中ptrSize是指一个指针的大小,在64位机上是8
进行内存对齐之后,新 slice 的容量是要
大于等于 按照前半部分生成的newcap。向 Go 内存管理器申请内存,将老 slice 中的数据复制过去,并且将 append 的元素添加到新的底层数组中。
- 当 slice 作为函数参数时,就是一个普通的结构体。其实很好理解:若直接传 slice,在调用者看来,实参 slice 并不会被函数中的操作改变;若传的是 slice 的指针,在调用者看来,是会被改变原 slice 的。
不管传的是 slice 还是 slice 指针,如果改变了 slice 底层数组的数据,会反应到实参 slice 的底层数据。为什么能改变底层数组的数据?很好理解:底层数据在 slice 结构体里是一个指针,尽管 slice 结构体自身不会被改变,也就是说底层数据地址不会被改变。 但是通过指向底层数据的指针,可以改变切片的底层数据,没有问题。
make new
make 支持slice map channel 内存创建,返回值本身。
会初始化
slice、chan、map 类型的内部数据结构,new 函数并不会。