1.切片的底层实现:
type slice struct {
array unsafe.Pointer //数组指针
len int //切片长度
cap int //切片容量
}
声明slice后,此时该slice还未被分配空间,但是可以用append插入数据,append操作会自动给slice扩容
var slice []int
//slice[0] = 1 //panic: runtime error: index out of range
slice = append(slice, 1)
fmt.Println(slice)// 输出:[1]
2.切片的扩容:
只有执行append函数时,切片才会扩容。
当切片的长度len等于容量cap时,再添加数据就会将当前的数据复制到一个更大的数组中,如果数据长度在1024以下的每次扩容到原数组长度的2倍。超出1024时扩容为原来的1.25倍-1.33倍(1/3-1/4之间)(官网给出的是1/4左右)。增加的少了是怕占用的内存过大。
注意以下操作:
s := []int{7} //[7]
s = append(s, 1) //[7 1]
s = append(s, 2) //此时切片s的len=3;cap=3 [7 1 2]
x := append(s, 10) //此时是对 切片[7 1 2]的操作
y := append(s, 20) //此时也是对 切片[7 1 2]的操作
// x,y两个切片底层的数组指针 指向的是同一块数组存储空间 所以上述语句执行完后 x,y的输出是一样的。
// 但是注意当cap容量发生变化时,数组指针(地址)改变
// 例如 y := append(s, 20,30)时x,y的输出就会不一样 [7 1 2 10] [7 1 2 20 30]
fmt.Println(s, x, y)
//输出结果:[7 1 2] [7 1 2 20] [7 1 2 20]
3.切片作为函数参数传参:
- 切片作为函数参数是地址传递(引用传递), 形参可以修改实参的值。
- 切片的截取操作还是在源切片上操作,所以修改截取后的切片中的值 会影响源切片中对应下标的值。
- 切片作为函数参数传参时,如果在传入的函数中使用append函数使切片扩容量(切片容量大小发生改变),此时系统底层会把原数组的值复制给一个新的len更大的数组。所以对应切片指向数组的指针(地址)就会改变。这会导致形参改变,实参并未改变。
- 切片作为函数参数传递,为避免底层数组发生改变,可以传递地址