Slice(切片)
定义
切片的概念在python
中也存在这个概念,在go
中,切片是对数组的一个连续片段的引用,所以切片是一个引用类型,它的内部结构包含地址
、长度
和容量
。
版本号:go version go1.20 darwin/arm64
runtime/slice.go
type slice struct {
array unsafe.Pointer
len int
cap int
}
其中:
array
是底层数组指针,指向该切片的第一个元素len
是切片的长度cap
是切片的容量
变量声明
主要是使用make
关键字去进行切片的创建
// make([]T, len, cap)
// len: 长度
// cap: 容量
// T: 类型
var s1 []int = make([]int, 5, 10)
var s2 []int =make([]int, 5)
容量未声明,则与长度相等
s1
在初始化后,长度为5,容量为10,值为[0,0,0,0,0]
s2
在初始化后,长度为5,容量为5,值为[0,0,0,0,0]
扩容
扩容的前提是当前切片容量不足以容纳增加后的数据。
相关函数:
runtime/slice.go/growslice
newcap := oldCap
doublecap := newcap + newcap
if newLen > doublecap {
newcap = newLen
} else {
const threshold = 256
if oldCap < threshold {
newcap = doublecap
} else {
// Check 0 < newcap to detect overflow
// and prevent an infinite loop.
for 0 < newcap && newcap < newLen {
// Transition from growing 2x for small slices
// to growing 1.25x for large slices. This formula
// gives a smooth-ish transition between the two.
newcap += (newcap + 3*threshold) / 4
}
// Set newcap to the requested cap when
// the newcap calculation overflowed.
if newcap <= 0 {
newcap = newLen
}
}
}
-
如果当前容量扩充两倍后,仍然无法容纳新的数据,那么新的容量就为新的长度
oldlen+num
-
扩充两倍后,可以容纳新的数据,再判断
oldlen
是否>=
1024,大于则扩充1.25倍,小于则扩充两倍
函数传参
切片作为函数参数传递时,传递的是切片的引用,所以在函数内部修改切片的值,会影响到函数外部的切片值。
简单来说,切片a作为参数传递时,编译器会根据a的地址、长度、容量,创建一个新的切片b,然后将b作为参数传递给函数,所以在函数内部修改切片的值,a和b指向的底层数组x一致。修改b的元素,本质上修改底层数组的数据,所以a的值也会发生变化。
但如果切片b发生扩容,情况又不一样
更新切片
a=[]int{1,2,3,4,5}
func UpdateSlice(b []int) {
b[0] = 100
}
因为只是更新切片数据,修改b的元素,本质上修改底层数组的数据,a和b都还是指向同一块内存空间,所以a的值也会发生变化。
扩容切片
a=[]int{1,2,3,4,5}
func GrowSlice(b []int) {
b = append(b, []int{1, 2, 3, 4, 5, 6, 7, 8, 9}...)
fmt.Println(b)
}
b进行了append
操作,容量不足,发生扩容,编译器重新分配一块合适的内存空间,并修改了b
的指向,此时再去修改b
的值,不会影响到a
的值。