切片定义
type slice struct {
array unsafe.Pointer
len int
cap int
}
切片是对数组的一个连续“片段”的引用,切片是引用类型;切片的出现,解决了数组的不足:固定的元素个数不可变和传值机制下导致的开销较大的问题;切片中可以通过内置函数 append,动态地向切片中添加元素。切片共三个字段,array、len、cap,分别表示指向底层的数组指针、切片长度、切片容量即底层数组长度。
切片扩容
在1.18之前,如果新申请的容量大于当前容量的2倍,则直接把申请的容量作为新的容量;如果申请的容量小于当前容量2倍,则当切片容量小于1024时,会直接把当前容量的2倍作为新的容量,当大于等于1024时,则会把当前容量的1.25倍作为新的容量。
在1.18之后,如果新申请的容量大于当前容量的2倍,则直接把申请的容量作为新的容量;如果申请的容量小于当前容量2倍,则当切片容量小于256时,会直接把当前容量的2倍作为新的容量,当大于等于256时,则会把当前容量的1.25倍 + 192作为新的容量(192 = 256 * 3 / 4, newcap += (newcap + 3*threshold) / 4)。
源码部分runtime/slice.go中的growslice(et *_type, old slice, cap int)
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
} else {
const threshold = 256
if old.cap < threshold {
newcap = doublecap
} else {
// Check 0 < newcap to detect overflow
// and prevent an infinite loop.
for 0 < newcap && newcap < cap {
// 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 = cap
}
}
}
Slice扩容踩坑
func TestSlice(T *testing.T) {
s := make([]int, 2, 2)
s[0] = 10
fmt.Printf("append s[0] is: %d\n", 10)
fmt.Printf("slice s is: %v\n", s)
s1 := s
fmt.Printf("slice s1 is: %v\n", s1)
s1[0] = 100
fmt.Printf("append s1[0] is: %d\n", 100)
fmt.Printf("slice s is: %v\n", s)
fmt.Printf("slice s1 is: %v\n", s1)
s[1] = 200
fmt.Printf("append s1[1] is: %d\n", 200)
fmt.Printf("slice s is: %v\n", s)
fmt.Printf("slice s1 is: %v\n", s1)
s1 = append(s1, 300)
fmt.Printf("append s1 is: %d\n", 300)
fmt.Printf("slice s is: %v\n", s)
fmt.Printf("slice s1 is: %v\n", s1)
s1[0] = -100
fmt.Printf("append s1[0] is: %d\n", -100)
fmt.Printf("slice s is: %v\n", s)
fmt.Printf("slice s1 is: %v\n", s1)
}
上述代码最终输出为:
append s[0] is: 10
slice s is: [10 0]
slice s1 is: [10 0]
append s1[0] is: 100
slice s is: [100 0]
slice s1 is: [100 0]
append s1[1] is: 200
slice s is: [100 200]
slice s1 is: [100 200]
append s1 is: 300
slice s is: [100 200]
slice s1 is: [100 200 300]
append s1[0] is: -100
slice s is: [100 200]
slice s1 is: [-100 200 300]
可以看到最开始定义切片s初始化为长度和容量都是2,把s赋值给s1,之后对s1的前两次操作都修改了s,之后s1 = append(s1, 300)之后,对s1的修改并没有改变s。这是因为s1扩容了,指向底层数组的指针发生了改变。