目录
一、切片介绍
切片是围绕动态数组的概念构建的,可以按需自动增长和缩小。切片的动态增长是通过内置函数 append() 来实现的,这个函数可以快速且高效地增长切片,也可以通过对切片再次切割,缩小一个切片的大小。因为切片的底层也是在连续的内存块中分配的,所以切片还能获得索引、迭代以及为垃圾回收优化的好处。形式上类似于python中的list的,但是没有提供python中list那么多的可用方法,也情有可原。
二、切片使用
-
切片创建
切片创建可以通过make函数,如创建一个整形切片
//长度len=5,cap=5,使用make创建切片,不指定第3个参数时,默认切片容量等于长度mSlic := make([]int,5)
此时,打印该切片,切片值为[0,0,0,0,0],即默认初始化为0.
除了用make外还能用指定切片元素创建,如下
//长度和容量依旧相等,为5
mSlic := []int{1,2,3,4,5}
-
数组和切片的区别
数组和切片最本质的区别就是数组是值类型,切片是引用,且数组定长无法扩容,而切片可以。
//数组初始化,一个长度为5的定长数组
marray := [5]{1,2,3,4,5}
//不定长数组
marray := [...]{1,2,3,4,5}
可以看到数组与切片的创建形式完全不同。
-
切片的实现原理
切片的实现其实是通过数组实现的,前面说过切片是引用,那么怎么引用?引用的又是谁?如下图
中间为数组,上面为切片mSlic的创建,mSlic的地址指向了数组,即切片是指向底层数组空间的,
当用切片mSlic去创建切片nSlic时,将地址mSlic地址向后移动,返回新的地址即为nSlic的空间,
可以看到,切片创建切片时两个切片mSlic和nSlic共享同一个数组空间,此时若是nSlic[1] = 70,那么读者觉得mSlic会发生什么呢?
没错,和你心里想 的一样,mSlic[2]会从30-->70,直接上代码展示,如下
func slic_dist() { mSlic := []int{10, 20, 30, 40, 50, 60} // 创建新的切片,其长度为 2 个元素,容量为 5 nSlic := mSlic[1:3] nSlic[1] = 70 fmt.Println("nSlic:", nSlic) fmt.Println("mSlic:", mSlic) //刚分配好切片的地址 fmt.Printf("%v,%p\n", nSlic, nSlic) // 使用原有的容量来分配一个新元素 // 将新元素赋值为 80 nSlic = append(nSlic, 80) fmt.Printf("%v,%p\n", nSlic, nSlic) //继续增加元素90 nSlic = append(nSlic, 90) fmt.Printf("%v,%p\n", nSlic, nSlic) //继续增加元素100 nSlic = append(nSlic, 100) fmt.Printf("%v,%p\n", nSlic, nSlic) //继续增加元素110,地址发生变化 nSlic = append(nSlic, 110) fmt.Printf("%v,%p\n", nSlic, nSlic) }
结果:
nSlic: [20 70]
mSlic: [10 20 70 40 50 60]
[20 70],0xc00000a308
[20 70 80],0xc00000a308
[20 70 80 90],0xc00000a308
[20 70 80 90 100],0xc00000a308
[20 70 80 90 100 110],0xc0000140f0从上述示例中可发现,确实和你们想的一样,原切片mSlic的第3个元素值随新切片nSlic的第2个值变化而变化,从30-->70,也充分说明了切片是依赖数组实现的,
且依赖切片创建的切片共享同一个底层数组空间,还有一点需要注意的是用切片创建切片,那么新切片不能超过原切片的容量,比如mSlic的cap=6,那么创建
新切片nSlic时,nSlic的cap<6,必须小于6,这是由于切片创建切片的方式是基于原来切片大小来的,而不是开辟新空间。
后面的地址打印是不断给新切片nSlic增加元素,nSlic地址的变化,你会发现当增加至第4个元素即nSlic元素总数为6时nSlic地址发生了变化,从0xc00000a308-->0xc0000140f0,
说明nSlic重新指向了一个新的数组空间,而将原nSlic指向数组中的值复制到了新数组空间,完成了nSlic的内存重新分配。
以上便是切片的一些基础及实现总结,没有好的画图软件,只能用画图工具来替代,读者见谅!
这里留个问题,大家猜猜最后mSlic的值是什么样的呢?可以自己尝试下。