一文详解Go语言中切片的底层原理.docx

一文详解Go语言中切片的底层原理

目录切片的函数传值切片动态扩容机制切片操作对数组的影响使用归纳本文总结大家好,我是二条,在上一篇我们学习了轻松理解Go中的内存逃逸问题,今天接着我们学习Go中切片的相关知识。本文不会单独去讲解切片的基础语法,只会对切片的底层和在开发中需要注意的事项作分析。

在Go语言中,切片作为一种引用类型数据,相对数组而言是一种动态长度的数据类型,使用的场景也是非常多。但在使用切片的过程中,也有许多需要注意的事项。例如切片函数传值、切片动态扩容、切片对底层数组的引用问题等等。今天分享的主题,就是围绕切片进行。

切片的函数传值

切片作为一种引用数据类型,在作为函数传值时,如果函数内部对切片做了修改,会影响到原切片上。

package?main

import?fmt

func?main()?{

?sl1?:=?make([]int,?10)

?for?i?:=?0;?i??i++?{

?fmt.Println(切片sl1的值是,?sl1)

?change(sl1)

?fmt.Println(切片sl2的值是,?sl1)

func?change(sl?[]int)?{

?sl[0]?=?100

?fmt.Println(形参sl切片的值是,?sl)

}

打印上述代码:

切片sl1的值是[12345678910]

形参sl切片的值是[1002345678910]

切片sl2的值是[1002345678910]

通过上面的结果,不难看出来,在函数change()中修改了切片,原切片的小标0的值也发生了改变。这是因为切片是一种引用类型数据,在传递到函数change()时,使用的都是相同的底层数组(切片底层本质仍是一个数组)。因此,底层数组的值改变了,就会影响到其他指向该数组的切片上。

针对上述的问题,有什么解决方案,使得传递切片,不会影响原切片的值呢?可以采用切片复制的方式,重新创建一个新的切片当做函数的参数进行传递。

package?main

import?fmt

func?main()?{

?sl1?:=?make([]int,?10)

?for?i?:=?0;?i??i++?{

??sl1[i]?=?i?+?1

?fmt.Println(切片sl1的值是,?sl1)

?//?创建一个新的切片,当做参数传递。

?sl2?:=?make([]int,?10)

?copy(sl2,?sl1)

?change(sl2)

?fmt.Println(切片sl2的值是,?sl1)

func?change(sl?[]int)?{

?sl[0]?=?100

?fmt.Println(形参sl切片的值是,?sl)

}

打印上述代码:

切片sl1的值是[12345678910]

形参sl切片的值是[1002345678910]

切片sl2的值是[12345678910]

通过上述运行结果,在change函数中,对切片下标为0做了值修改,对切片sl1的值没有影响。

切片动态扩容机制

在Go中,切片是一种动态长度的引用数据类型。当切片的容量不足以容纳新增加的元素时,底层会实现自动扩容用来存储新添加的元素。先查看下面的一段实例代码,证明切片存在动态扩容。

package?main

import?fmt

func?main()?{

?var?sl1?[]int

?fmt.Println(切片sl1的长度是,?len(sl1),?,容量是,?cap(sl1))

?for?i?:=?0;?i??i++?{

??sl1?=?append(sl1,?i)

??fmt.Println(切片sl1的长度是,?len(sl1),?,容量是,?cap(sl1))

}

打印上述代码:

切片sl1的长度是0,容量是0

切片sl1的长度是1,容量是1

切片sl1的长度是2,容量是2

切片sl1的长度是3,容量是4

切片sl1的长度是4,容量是4

切片sl1的长度是5,容量是8

切片sl1的长度是6,容量是8

切片sl1的长度是7,容量是8

切片sl1的长度是8,容量是8

切片sl1的长度是9,容量是16

切片sl1的长度是10,容量是16

可以看出,切片的长度是随着for操作,依次递增。但切片的容量就不是依次递增,从明面上看,有点像以2的倍数在增加。具体增加的规律是怎么样的呢?

要弄明白Go中的切片是如何实现扩容的,这就需要关注一下Go的版本。

在Go的1.18版本以前,是按照如下的规则来进行扩容:

1、如果原有切片的

文档评论(0)

1亿VIP精品文档

相关文档