golang slice

发布时间 2023-11-15 14:08:10作者: _小孟同学

slice 是 golang 的切片,动态数组

底层结构

// runtime/slice.go
type slice struct {
	array unsafe.Pointer // 底层数组
	len   int
	cap   int
}

golang 的 slice 底层是一个数组,也就是可以通过 &s[0]来获取底层数组的地址,len 记录的当前,cap 记录的是底层数组的大小也就是当前 slice 的容量

初始化

var num1 []int // num1此时为 nil
num2 := make([]int,4) //len = 4 cap = 4
num3 := make([]int,3,7) // len = 3 cap = 7
num4 := []int{1,2,3} // len = 3 cap = 3

或从另一个slice新建

s1 := []int{1,2,3,4,5} // s1 len = 5 cap = 5
s2 := s1[:] // s2 len = 5 cap = 5
s3 := s1[:3] // s3 len = 3 cap = 5
s4 := s1[:2:4] // s4 len = 2 cap = 4

以上的 s2,s3,s4 底层数组指向的都是 s1 的底层数组起始位置

函数传参

slice 作为函数传参时候传递的是 slice 的 struct 的拷贝,但是底层数组是作为struct 内部的一个变量是不变的,所以在函数间传递时候更改 slice 的某些变量会影响原 slice

扩容

在 slice 调用 append 方法时,如果添加后的容量大于 cap,那么就会发生扩容

扩容规则

在1.18版本之前
当原 cap < 1024 时候,每次扩容会扩容成两倍;当 cap > 1024 时候,会扩容成原容量的 1.25 倍
在1.18 版本之后
当原 cap < 256 时候,每次扩容会扩容成原容量的两倍;当 cap > 256,会扩容成原容量的 newcap = oldcap+(oldcap+3*256)/4 倍,所以当原容量是 256 时,也会扩容成 512;但是在实际扩容过程中不会精确地这个值,通常会进行一次内存对齐,所以会有出入;

扩容过程

先申请 newcap 大小的数组,然后把原数组全量拷贝过去,然后再进行其他的操作

难点

slice 的难点主要是还是传参,和传参后的 append 触发扩容
基本原则就一个,没发生扩容的情况下,会改变原数组;如果发生扩容,则不会改变原数组

参考

数组与切片有什么异同