3week-4切片
一.切片的特性
- 可以切除来一个新的子片
- 长度可变,长度元素个数
- 容量可变,长度和容量可以不一样
- 底层用数组,(顺序表访问快)
- 推荐使用make定义,可以指定初始容量大小,减少频发扩容
- 切片复制的都是header,函数return的也是header,
len控制各个共同使用底层数组的可见范围
cap没有扩容之前还有几个位置可用
首地址觉得容量还有多少,如果首地址偏移1,容量就会-1,偏移2,容量就会-2 - 判断切片是否是空
len(s) == 0而不是用s == nil
二.切片定义
//1.切片定义方式1---字面量定义
- 适用于少了元素
var s0 = []int{1, 3, 5, 7, 9} //{}里面是字面量;字面量定义就是初始化的时候把元素写好了,长度为5,容量为5
fmt.Printf("s0的类型:%T s0的值:%[1]v s0的长度%d s0的容量%d\n", s0, len(s0), cap(s0))
结果

2.声明空切片方式1---不常用
- 空切片随时准备膨胀
- %P输出内存地址,否则输出%v
var s1 []int // 仅是声明,零值填充,长度为0,容量为0
fmt.Println(s1, len(s1), cap(s1), &s1)
fmt.Printf("%p %[1]T", &s1)
fmt.Printf(" %p %[1]T", s1)

3.推导定义声明切片2---不常用
var s2 = []int{} //生成[]int切片,长度和容量都是0
fmt.Printf("s2的地址是:%p s2的类型是:%[1]T\n", &s2)
结果:

4.make声明切片1
- 用的多,推荐使用 make可以给内建容器开辟内存空间
- 适用于很多个元素
切片的参数

var s3 = make([]string, 0) // 切片使用make,第二个参数0表示长度.为0的切片,没有元素容量也为0
fmt.Println(s3, len(s3), cap(s3))
结果

5.make定义切片2
1.切片参数

s4 := make([]string, 0, 5) //切片使用make,第2个参数0表示长度为0, 第三个参数表示容量为5
s4 := make([]string, 10) //如果只写了第一个参数,长度和容量相同都是10
fmt.Println(s4, len(s4), cap(s4))

6.先用make定义100个.然后初始化几个
三.切片内存模型
1.

- append返回的就是header
- 指针: 指向底层数组
- 当前切片的元素个数
- cap当前切片的容量,容量是能容纳多少元素不爆
2.数组撑爆问题

结果

四切片append
- append函数的作用: 用于切片,对某切片追加元素,不会修改切片的header,返回值可以用的,返回值是新header,新header里面的指针可能变了,len,cap也有可能变了
- 如果append追加到s0,把s0撑爆,会产生新的数组
1.append1---切片共享底层数组分析
func main() {
var s0 = make([]int, 3, 5) //长度为3(用了3个元素),容量为5(空着2个元素;值=零)
fmt.Printf("s0 :%p, %p, %-2d, %-2d,%v\n", &s0, &s0[0], len(s0), cap(s0), s0) //0xc000004078, 0xc00000a420, 3 , 5 ,[0 0 0]
s1 := append(s0, 1, 2) //往s0里追加2个元素是1,2;由于s0长3,容量5,虽然增加了,但没有撑爆s0,append不更新s0的heder
fmt.Printf("s0 :%p, %p, %-2d, %-2d,%v\n", &s0, &s0[0], len(s0), cap(s0), s0) //[0 0 0]
fmt.Printf("s1 :%p, %p, %-2d, %-2d,%v\n", &s1, &s1[0], len(s1), cap(s1), s1) //[0 0 0 1 2]append追加不会更新s0的herder
// fmt.Println(s0[2])
}
- 结果:

- 显示元素分析图

2.append2---切片共享底层数组分析
s0,s1,s2自己看自己的长度,看底层数组s0的容量;;如果长度撑爆底层数组,如果谁撑爆数组,就开辟
func main() {
s2 := append(s0, -1)
//1.因为s0[0 0 0],len(3) cap(5); append,向 s0 追加-1,没有撑爆s0,
//2.那么不用新生成底层数组,使用s0底层数组进行追加,[0 0 0 -1]len4,cap5
//3.append返回新header给s2; s3的header指针指向s0
fmt.Printf("s0 :%p, %p, %-2d, %-2d,%v\n", &s0, &s0[0], len(s0), cap(s0), s0) //s0=[0 0 0] len=3 cap=5
fmt.Printf("s1 :%p, %p, %-2d, %-2d,%v\n", &s1, &s1[0], len(s1), cap(s1), s1) //s1=[0 0 0 -1 2] len=5 cap=5
fmt.Printf("s2 :%p, %p, %-2d, %-2d,%v\n", &s2, &s2[0], len(s2), cap(s2), s2) //s2=[0 0 0 -1] len4, cap5
}
- 结果

- 显示元素分析图

3.append3---切片撑爆底层数字组分析
func main() {
s2 := append(s0, -1)
//1.因为s0[0 0 0],len(3) cap(5); append,向 s0 追加-1,没有撑爆s0,
//2.那么不用新生成底层数组,使用s0底层数组进行追加,[0 0 0 -1]len4,cap5
//3.append返回新header给s2; s3的header指针指向s0
fmt.Printf("s0 :%p, %p, %-2d, %-2d,%v\n", &s0, &s0[0], len(s0), cap(s0), s0) //s0=[0 0 0] len=3 cap=5
fmt.Printf("s1 :%p, %p, %-2d, %-2d,%v\n", &s1, &s1[0], len(s1), cap(s1), s1) //s1=[0 0 0 -1 2] len=5 cap=5
fmt.Printf("s2 :%p, %p, %-2d, %-2d,%v\n", &s2, &s2[0], len(s2), cap(s2), s2) //s2=[0 0 0 -1] len4, cap5
fmt.Println("~~~~~~~~~~~~~~~~~~~~~~~")
s3 := append(s2, 3, 4, 5)
//1.因为s2=[0 0 0 -1] len(4) cap(5); append向 s2 追加3,4,5; 撑爆了s2
//2.那么生成新的底层数组,复制s2的元素到新数组,插入3,4,5;[0 0 0 -1 3 4 5]len7,cap10
//3.append返回新heder给s3; s3指针指向新底层数组地址
fmt.Printf("s0 :%p, %p, %-2d, %-2d,%v\n", &s0, &s0[0], len(s0), cap(s0), s0) //s0=[0 0 0] len=3 cap=5
fmt.Printf("s1 :%p, %p, %-2d, %-2d,%v\n", &s1, &s1[0], len(s1), cap(s1), s1) //s1=[0 0 0 -1 2] len=5 cap=5
fmt.Printf("s2 :%p, %p, %-2d, %-2d,%v\n", &s2, &s2[0], len(s2), cap(s2), s2) //s2=[0 0 0 -1] len=4, cap=5
fmt.Printf("s3 :%p, %p, %-2d, %-2d,%v\n", &s3, &s3[0], len(s3), cap(s3), s3) //s3=[0 0 0 -1 3 4 5] len=7 cap=10容量翻倍5X2=10
}
-
结果

-
显示元素分析

append,返回的header给底层数组分析
s3 = append(s3, 6, 7, 8, 9)
fmt.Printf("s3 :%p, %p, %-2d, %-2d,%v\n", &s3, &s3[0], len(s3), cap(s3), s3) //s3=[0 0 0 -1 3 4 5] len=7 cap=10
-
结果

-
显示header地址分析,S3的地址没变,值变了


浙公网安备 33010602011771号