我们知道go的slice是引用类型,其底层结构有三个变量分别是len,cap和底层数组的指针,今天就用各种实例跑一下这3个变量的变化情况
1.切片初始化
1.1一般初始化方式
a := []int{1,2,3,4,5,6}
fmt.Printf("a的cap为%d,a的len为%d,a为%d,a的地址为%p,a数组指针的地址为%d\n",cap(a),len(a),a,&a,&a[0])
执行结果为:
a的cap为6,a的len为6,a为[1 2 3 4 5 6],a的地址为0x1400011e018,a数组指针的地址为1374390779904
- 初始化时的cap 等于 len 等于初始化长度;
1.2当初始化为空切片时
a := []int{}
fmt.Printf("a的cap为%d,a的len为%d,a为%d,a的地址为%p\n",cap(a),len(a),a,&a)
其结果为:
a的cap为0,a的len为0,a为[],a的地址为0x1400011e018
- 初始化为空切片时 ,cap 等于 len 等于0;
1.3采用make的方式进行初始化
a := make([]int,4,5)
fmt.Printf("a的cap为%d,a的len为%d,a为%d,a的地址为%p\n",cap(a),len(a),a,&a)
其结果为:
a的cap为5,a的len为4,a为[0 0 0 0],a的地址为0x1400011e018
- 采用make初始化切片有3个参数,类型,len,cap,切片里的值会被初始化成对应类型的零值,cap 参数可以省略,默认和len参数相同;
1.4采用var定义切片:
var a []int
fmt.Printf("a的cap为%d,a的len为%d,a为%d,a的地址为%p\n",cap(a),len(a),a,&a)
其结果为:
a的cap为0,a的len为0,a为[],a的地址为0x1400011e018c
- 采用var定义切片默认len = cap = 0;
2.append操作
2.1 cap足够时
a := make([]int, 3, 6)
fmt.Printf("a的cap为%d,a的len为%d,a为%d,a的地址为%p,a数组的地址为%p\n",cap(a),len(a),a,&a,&a[0])
a = append(a,1)
fmt.Printf("a的cap为%d,a的len为%d,a为%d,a的地址为%p,a数组的地址为%p\n",cap(a),len(a),a,&a,&a[0])
其结果为:
a的cap为6,a的len为3,a为[0 0 0],a的地址为0x1400011e018,a数组的地址为0x14000130000
a的cap为6,a的len为4,a为[0 0 0 1],a的地址为0x1400011e018,a数组的地址为0x14000130000d
- 当容量足够时,进行append操作直接修改其len并在a数组后进行追加即可;
2.2 cap足够时追加多个数据
a := make([]int, 3, 6)
b := []int{1,2}
fmt.Printf("a的cap为%d,a的len为%d,a为%d,a的地址为%p,a数组的地址为%p\n",cap(a),len(a),a,&a,&a[0])
a = append(a,b...) // 等同于a = append(a,1,2)
fmt.Printf("a的cap为%d,a的len为%d,a为%d,a的地址为%p,a数组的地址为%p\n",cap(a),len(a),a,&a,&a[0])
其结果为:
a的cap为6,a的len为3,a为[0 0 0],a的地址为0x14000198000,a数组的地址为0x1400019a000
a的cap为6,a的len为5,a为[0 0 0 1 2],a的地址为0x14000198000,a数组的地址为0x1400019a000d
- **当容量足够时,**进行append操作追加多个数据,结果和多次追加1个数据结果相同;
2.3 cap不足时追加数据
a := make([]int, 3)
fmt.Printf("a的cap为%d,a的len为%d,a为%d,a的地址为%p,a数组的地址为%p\n",cap(a),len(a),a,&a,&a[0])
a = append(a,1)
fmt.Printf("a的cap为%d,a的len为%d,a为%d,a的地址为%p,a数组的地址为%p\n",cap(a),len(a),a,&a,&a[0])
其结果为:
a的cap为3,a的len为3,a为[0 0 0],a的地址为0x1400000c030,a数组的地址为0x14000016090
a的cap为6,a的len为4,a为[0 0 0 1],a的地址为0x1400000c030,a数组的地址为0x14000014240d
- 当容量不足时,进行append操作,会发生扩容,此时的数组地址发生变化,cap变为原来的2倍(不完全是2倍,可能会因为字节对齐的原因在2倍的基础上略有偏差);
2.4cap不足时追加多个数据扩容2倍后仍然cap不足时
a := make([]int, 4)
fmt.Printf("a的cap为%d,a的len为%d,a为%d,a的地址为%p,a数组的地址为%p\n",cap(a),len(a),a,&a,&a[0])
a = append(a,1,2,3,4,5)
fmt.Printf("a的cap为%d,a的len为%d,a为%d,a的地址为%p,a数组的地址为%p\n",cap(a),len(a),a,&a,&a[0])
其结果为:
a的cap为4,a的len为4,a为[0 0 0 0],a的地址为0x14000114018,a数组的地址为0x14000124020
a的cap为10,a的len为9,a为[0 0 0 0 1 2 3 4 5],a的地址为0x14000114018,a数组的地址为0x1400012a000
- cap不足时追加多个数据扩容2倍后仍然cap不足时,会继续扩容直到cap>len;(此时不是整倍的扩容了);
2.5 cap追加多个参数时的追加方式
a := make([]int, 3, 4)
b := append(a,1)
fmt.Printf("a的cap为%d,a的len为%d,a为%d,a的地址为%p,a数组的地址为%p\n",cap(a),len(a),a,&a,&a[0])
fmt.Printf("b的cap为%d,b的len为%d,b为%d,b的地址为%p,b数组的地址为%p\n",cap(b),len(b),b,&b,&b[0])
a = append(a,2,3)
fmt.Printf("a的cap为%d,a的len为%d,a为%d,a的地址为%p,a数组的地址为%p\n",cap(a),len(a),a,&a,&a[0])
fmt.Printf("b的cap为%d,b的len为%d,b为%d,b的地址为%p,b数组的地址为%p\n",cap(b),len(b),b,&b,&b[0])
其结果为:
a的cap为4,a的len为3,a为[0 0 0],a的地址为0x1400011e018,a数组的地址为0x1400012e020
b的cap为4,b的len为4,b为[0 0 0 1],b的地址为0x1400011e030,b数组的地址为0x1400012e020
a的cap为8,a的len为5,a为[0 0 0 2 3],a的地址为0x1400011e018,a数组的地址为0x14000124080
b的cap为4,b的len为4,b为[0 0 0 1],b的地址为0x1400011e030,b数组的地址为0x1400012e020
- 对a进行append时,当a还有空间到那时a的空间不足时,不会在原空间惊醒修改;
3 多切片公用空间
3.1多切片共用相同空间
a := make([]int, 3, 8)
b := append(a,1)
fmt.Printf("a的cap为%d,a的len为%d,a为%d,a的地址为%p,a数组的地址为%p\n",cap(a),len(a),a,&a,&a[0])
fmt.Printf("b的cap为%d,b的len为%d,b为%d,b的地址为%p,b数组的地址为%p\n",cap(b),len(b),b,&b,&b[0])
a = append(a,2,3)
fmt.Printf("a的cap为%d,a的len为%d,a为%d,a的地址为%p,a数组的地址为%p\n",cap(a),len(a),a,&a,&a[0])
fmt.Printf("b的cap为%d,b的len为%d,b为%d,b的地址为%p,b数组的地址为%p\n",cap(b),len(b),b,&b,&b[0])
b[2] = 10
fmt.Printf("a的cap为%d,a的len为%d,a为%d,a的地址为%p,a数组的地址为%p\n",cap(a),len(a),a,&a,&a[0])
fmt.Printf("b的cap为%d,b的len为%d,b为%d,b的地址为%p,b数组的地址为%p\n",cap(b),len(b),b,&b,&b[0])
其结果为:
a的cap为8,a的len为3,a为[0 0 0],a的地址为0x1400011e018,a数组的地址为0x14000124040
b的cap为8,b的len为4,b为[0 0 0 1],b的地址为0x1400011e030,b数组的地址为0x14000124040
a的cap为8,a的len为5,a为[0 0 0 2 3],a的地址为0x1400011e018,a数组的地址为0x14000124040
b的cap为8,b的len为4,b为[0 0 0 2],b的地址为0x1400011e030,b数组的地址为0x14000124040
a的cap为8,a的len为5,a为[0 0 10 2 3],a的地址为0x1400011e018,a数组的地址为0x14000124040
b的cap为8,b的len为4,b为[0 0 10 2],b的地址为0x1400011e030,b数组的地址为0x14000124040
- 多切片共用相同的数组空间时,其结果会相互影响但是其len值不会相互影响;
3.2 切片截取
a := make([]int, 3, 8)
a[0] = 0
a[1] = 1
a[2] = 2
b := a[1:4]
fmt.Printf("a的cap为%d,a的len为%d,a为%d,a的地址为%p,a数组的地址为%p\n",cap(a),len(a),a,&a,&a[0])
fmt.Printf("b的cap为%d,b的len为%d,b为%d,b的地址为%p,b数组的地址为%p\n",cap(b),len(b),b,&b,&b[0])
b = append(b,4,5,6,7,8,9)
fmt.Printf("a的cap为%d,a的len为%d,a为%d,a的地址为%p,a数组的地址为%p\n",cap(a),len(a),a,&a,&a[0])
fmt.Printf("b的cap为%d,b的len为%d,b为%d,b的地址为%p,b数组的地址为%p\n",cap(b),len(b),b,&b,&b[0])
其结果为:
a的cap为8,a的len为3,a为[0 1 2],a的地址为0x1400000c018,a数组的地址为0x1400001e080
b的cap为7,b的len为3,b为[1 2 0],b的地址为0x1400000c030,b数组的地址为0x1400001e088
a的cap为8,a的len为3,a为[0 1 2],a的地址为0x1400000c018,a数组的地址为0x1400001e080
b的cap为14,b的len为9,b为[1 2 0 4 5 6 7 8 9],b的地址为0x1400000c030,b数组的地址为0x1400005a070
- 切片进行截取时,数组容量的末尾和原切片数组末尾对齐,数组地址为截取单元的首地址,且可以超过其len的范围进行截取,当其中一个数组发生扩容时,另一个数组的地址不变;
总结
以上便是slice的各种操作时,其cap,len,和底层数组的变化实例,结果和相应的结论。