关于golang中的切片详解

之前对切片的理解是本身是一个指针,指针指向数组,因为数组是值赋值,所以数组作为函数参数传递时,会被复制一份,传递较大数组时会比较浪费内存。

之前的理解部分对,但是瑕疵和错误的地方太多。

详解slice结构体

切片本身是一个结构体,它是被golang封装过的结构体,所以使用起来和数组差不多,其结构体内容如下:

 

type slice struct {

    array unsafe.Pointer  // 指向数组的指针,核心所在

    len int                         // 切片长度,在长度范围内,都可以像数组那样更新索引中的值,在len范围外会报越界错误

    cap int                       // 容量,指针所指向的数组长度,必须大于等于len字段

}

 

在len字段长度范围的操作,和数组用法一致,例如

var array [10]int

slice:=make([]int,10,20)

slice[9]=10 // 在索引值小于10时,都可以直接进行赋值

array[9]=10

 

append函数和切片联系紧密,切片的赋值和append函数有关。

当需要给len字段长度之外的索引进行操作,需要使用到append函数,例如

// fmt.Println(slice[10])  // 输出 panic: runtime error: index out of range,索引超出范围之外了

slice=append(slice,5,5) // 可以一次加入多个值(本次操作,添加了两个值),也可以直接追加一个切片

fmt.Println(slice[10])  // 输出为5

 

进行append()操作,当len加上新加入的值个数大于cap时,则会触发扩容操作,即调用slice包的growslice进行扩容,扩容大致内容是:

len小于1000时,cap扩容两倍,创建一个2*cap长度的数组,将之前数组的内容赋值到新指针指向的数组,len>1000时,扩容因子为1.25倍。

 此时新的slice和老的slice不是同一个结构体,指向的数组也不相同,append函数返回的是新slice,cap为扩容后的长度,新的len=老的len+新加入的值个数

数组和切片的区别

切片相当于对数组的一层封装,扩容方面比较灵活,数组初始化以后数组长度就再也不能变化了,想要扩容只能主动手写函数。

在当函数参数进行值传递方面,slice维护了数组的指针,加上本身的两个int字段,一共长24个字节,作为值传递时比较轻量级,数组值传递取决于数组的长度,如果是1e6长度的int数组,则每次传递需要重新开辟8m的内存创建一个新的数组,对空间浪费较大。

如果在函数传入的参数是数组指针,也只是需要占用8字节的空间,一个指针8字节(64位系统),这时和使用slice没有什么区别,只是扩容的灵活度方面不如slice。

建议

在我看来,使用slice有些需要注意的点,如果初始化了一个len为5,cap为10的切片,索引的前五位(0~4)都是可以直接赋值的,在前面的5位还没使用时,不要使用append函数添加新的值,这样会造成切片前面五个值的浪费,而且他们的内容都为零值,可能会和你的预期不一致。

在不能确定切片的起始长度时,可以初始化为make([]int,0,10)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值