- 切片在声明和初始化的时候,可以选择设置长度(len)和容量(cap)
- 长度(len):切片的长度也可以指slice中的元素个数
- 容量(cap):从slice的起始位置到底层数组的长度末尾之间能容纳多少个元素。切片的最大容量。却len用于小于等于cap
- cap使用大于len,cap可以为后续slice的扩容起到到关键作用
- 接下来看一下源码部分
type slice struct {
array unsafe.Pointer // 指向底层数组的指针
len int // 切片的长度
cap int // 切片的容量
}
- 引用类型。slice为引用类型,多个slice共享同一底层数组
var data = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
_data := data
_data[6] = 600
fmt.Println("data:", data)
// data: [0 1 2 3 4 5 600 7 8 9]
fmt.Println("_data:", _data)
// _data: [0 1 2 3 4 5 600 7 8 9]
以上可以看出他们data和_data是共享一个底层数组
4.关于slice的扩容机制 当前slice长度小于等于256时,会进行一个翻倍扩容。 如果大于256时会按照 256+(256+3*256)/4这个算法去进行一个扩容。
5.slice和array的区别 slice:为引用传递,初始化时可以进行设置长度(len)和容量(cap),且支持扩容操作。 array:为值传递,固定长度。
6.当slice进行append操作 如果当前slice的长度大于容量时候,会进行扩容的情况下,还会涉及将原slice的数据复制到新分配的内存空间,以及更细切片的指针、长度容量。
7.接下来看看makeslice函数
// et *_type 元素类型的指针
func makeslice(et *_type, len, cap int) unsafe.Pointer {
// et.size 每个元素的大小(字节数)
// mem 切片容量所需的字节数
// math.MulUintptr 一个安全的乘法运算 避免溢出
// 进行安全检查 是否合法性
mem, overflow := math.MulUintptr(et.size, uintptr(cap))
// overflow 判断是否已出
// 所需内存大于 maxAlloc
// 判断长度是否合法 以及长度是否大于容量 进行合法判断
if overflow || mem > maxAlloc || len < 0 || len > cap {
// NOTE: Produce a 'len out of range' error instead of a
// 'cap out of range' error when someone does make([]T, bignumber).
// 'cap out of range' is true too, but since the cap is only being
// supplied implicitly, saying len is clearer.
// See golang.org/issue/4085.
// 判断长度是否合法
mem, overflow := math.MulUintptr(et.size, uintptr(len))
if overflow || mem > maxAlloc || len < 0 {
panicmakeslicelen()
}
panicmakeslicecap()
}
return mallocgc(mem, et, true)
}
- makeslice这个放方法中
- math.MulUintptr(et.size, uintptr(cap))
- 注意这里使用的uintptr(cap)容量和元素的大小(字节数)做安全的乘法运算
- 需要计算slice中元素所占用的内存是否合理,是否超出范围会出现溢出的情况。
- 判断长度(len)的合法性,还有一个就是之前说的len必须小于cap的情况。
- 如果以上条件满足任意一个会进行下一步判断
- math.MulUintptr(et.size, uintptr(len))
- 这里使用的是len和元素的大小(字节数)做安全的乘法运算
- 进入到if中的时候就报panic
- 若make合法将会进行mallocgc返回一个指针地址
mallocgc后边再进行分析,涉及到垃圾回收内存分类等知识。