Slice结构体定义
//slice 结构体定义如下
type slice struct {
array unsafe.Pointer //指向数据的首地址指针 8字节
len int //长度 4||8字节
cap int //容量 4||8字节
}
make Slice
// 初始化 slice
s1 := make([]int, 3, 5)
fmt.Printf("s1: s1 address:%p, data address: %p, len: %v, cap: %v,%v\n", &s1, unsafe.Pointer(&s1[0]), len(s1), cap(s1), s1)
// 输出 s1: s1 address:0xc000127e60, data address: 0xc000119b00, len: 3, cap: 5,[0 0 0]
// 添加元素, 不扩容, data address不变(不重新分配内存)
s1 = append(s1, 1, 2)
fmt.Printf("s1: s1 address:%p, data address: %p, len: %v, cap: %v, %v\n", &s1, unsafe.Pointer(&s1[0]), len(s1), cap(s1), s1)
// 输出 s1: s1 address:0xc000127e60, data address: 0xc000119b00, len: 5, cap: 5, [0 0 0 1 2]
// 添加元素, 扩容, data address变更(重新分配内存)
s1 = append(s1, 1, 2, 3, 4)
fmt.Printf("s1: s1 address:%p, data address: %p, len: %v, cap: %v, %v\n", &s1, unsafe.Pointer(&s1[0]), len(s1), cap(s1), s1)
// 输出 s1: s1 address:0xc000127e60, data address: 0xc00014dbd0, len: 9, cap: 10, [0 0 0 1 2 1 2 3 4]
arr := [10]int{0,1,2,3,4,5,6,7,8,9}
s3 := arr[2:6]
fmt.Printf("arr: arr address:%p, data address: %p, len: %v, cap: %v,%v\n", &arr, &arr[2], len(arr), cap(arr), arr)
// 输出 arr: arr address:0xc00014dc20, data address: 0xc00014dc30, len: 10, cap: 10,[0 1 2 3 4 5 6 7 8 9]
// 从数组索引出来的切片 s1[start:end], len=end-start, cap=len(arr) - start, 数据地址为数组第start元素地址
s2 := arr[2:5]
fmt.Printf("s2: s2 address:%p, data address: %p, len: %v, cap: %v,%v\n", &s2, &s2[0], len(s2), cap(s2), s2)
fmt.Printf("s3: s3 address:%p, data address: %p, len: %v, cap: %v,%v\n", &s3, &s3[0], len(s3), cap(s3), s3)
//输出 s2: s2 address:0xc000127f00, data address: 0xc00014dc30, len: 3, cap: 8,[2 3 4]
//输出 s3: s3 address:0xc000127ee0, data address: 0xc00014dc30, len: 4, cap: 8,[2 3 4 5]
// s2 添加元素, 不扩容, data address不变(不重新分配内存)
// s3 对应数据有影响
s2 = append(s2, 10, 11, 12)
fmt.Printf("s2: s2 address:%p, data address: %p, len: %v, cap: %v,%v\n", &s2, &s2[0], len(s2), cap(s2), s2)
fmt.Printf("s3: s3 address:%p, data address: %p, len: %v, cap: %v,%v\n", &s3, &s3[0], len(s3), cap(s3), s3)
//输出 s2: s2 address:0xc000127f00, data address: 0xc00014dc30, len: 6, cap: 8,[2 3 4 10 11 12]
//输出 s3: s3 address:0xc000127ee0, data address: 0xc00014dc30, len: 4, cap: 8,[2 3 4 10]
// s2 添加元素, 扩容, data address变更(重新分配内存)
// s3 对应数据无影响
// arr 对应数据无影响
s2 = append(s2, 10, 11, 12, 13, 14, 15, 16, 17)
fmt.Printf("arr: arr address:%p, data address: %p, len: %v, cap: %v,%v\n", &arr, &arr[2], len(arr), cap(arr), arr)
fmt.Printf("s2: s2 address:%p, data address: %p, len: %v, cap: %v,%v\n", &s2, &s2[0], len(s2), cap(s2), s2)
fmt.Printf("s3: s3 address:%p, data address: %p, len: %v, cap: %v,%v\n", &s3, &s3[0], len(s3), cap(s3), s3)
//输出 arr: arr address:0xc00014dc20, data address: 0xc00014dc30, len: 10, cap: 10,[0 1 2 3 4 10 11 12 8 9]
//输出 s2: s2 address:0xc000127f00, data address: 0xc00017d600, len: 14, cap: 16,[2 3 4 10 11 12 10 11 12 13 14 15 16 17]
//输出 s3: s3 address:0xc000127ee0, data address: 0xc00014dc30, len: 4, cap: 8,[2 3 4 10]
切片扩容大小
如果旧容量小于1024, 则新容量为原来2倍
如果旧容量大于1024, 则新容量按25%的速度增长
切片内存分配
在使用make()分配内存时, 会在底层默认生成一个对应cap大小的数组
如果make的cap小于64KB, 则会在栈内存分配空间
如果make的cap大于64KB, 则会在堆内存分配空间
总结
- 从同一个数组索引出来的切片, 底层公用同一份内存数据
- 在不引起扩容情况下,任何索引的切片引起的数据变更,也会影响数组及其他索引切片的数据
- 在引起扩容情况下,当前操作的切片会重新申请新的内存空间及数据拷贝(内存不大的情况下是原来2倍, 大的情况下是线性增加), 原来数据及切片不受影响