特别提醒,本文所涉及的源码是go1.21.0 darwin/amd64
文件位置:runtime/slice.go
1 数据结构 1 2 3 4 5 type slice struct { array unsafe.Pointer len int cap int }
array:指向存储元素内存空间的起始地点
len:切片的长度
cap:切片的容量
2 初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 func makeslice (et *_type, len , cap int ) unsafe.Pointer { mem, overflow := math.MulUintptr(et.Size_, uintptr (cap )) if overflow || mem > maxAlloc || len < 0 || len > cap { mem, overflow := math.MulUintptr(et.Size_, uintptr (len )) if overflow || mem > maxAlloc || len < 0 { panicmakeslicelen() } panicmakeslicecap() } return mallocgc(mem, et, true ) }
3 扩容 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 func growslice (oldPtr unsafe.Pointer, newLen, oldCap, num int , et *_type) slice { oldLen := newLen - num if newLen < 0 { panic (errorString("growslice: len out of range" )) } if et.Size_ == 0 { return slice{unsafe.Pointer(&zerobase), newLen, newLen} } newcap := oldCap doublecap := newcap + newcap if newLen > doublecap { newcap = newLen } else { const threshold = 256 if oldCap < threshold { newcap = doublecap } else { for 0 < newcap && newcap < newLen { newcap += (newcap + 3 *threshold) / 4 } if newcap <= 0 { newcap = newLen } } } var overflow bool var lenmem, newlenmem, capmem uintptr switch { case et.Size_ == 1 : lenmem = uintptr (oldLen) newlenmem = uintptr (newLen) capmem = roundupsize(uintptr (newcap)) overflow = uintptr (newcap) > maxAlloc newcap = int (capmem) case et.Size_ == goarch.PtrSize: lenmem = uintptr (oldLen) * goarch.PtrSize newlenmem = uintptr (newLen) * goarch.PtrSize capmem = roundupsize(uintptr (newcap) * goarch.PtrSize) overflow = uintptr (newcap) > maxAlloc/goarch.PtrSize newcap = int (capmem / goarch.PtrSize) case isPowerOfTwo(et.Size_): var shift uintptr if goarch.PtrSize == 8 { shift = uintptr (sys.TrailingZeros64(uint64 (et.Size_))) & 63 } else { shift = uintptr (sys.TrailingZeros32(uint32 (et.Size_))) & 31 } lenmem = uintptr (oldLen) << shift newlenmem = uintptr (newLen) << shift capmem = roundupsize(uintptr (newcap) << shift) overflow = uintptr (newcap) > (maxAlloc >> shift) newcap = int (capmem >> shift) capmem = uintptr (newcap) << shift default : lenmem = uintptr (oldLen) * et.Size_ newlenmem = uintptr (newLen) * et.Size_ capmem, overflow = math.MulUintptr(et.Size_, uintptr (newcap)) capmem = roundupsize(capmem) newcap = int (capmem / et.Size_) capmem = uintptr (newcap) * et.Size_ } if overflow || capmem > maxAlloc { panic (errorString("growslice: len out of range" )) } var p unsafe.Pointer if et.PtrBytes == 0 { p = mallocgc(capmem, nil , false ) memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem) } else { p = mallocgc(capmem, et, true ) if lenmem > 0 && writeBarrier.enabled { bulkBarrierPreWriteSrcOnly(uintptr (p), uintptr (oldPtr), lenmem-et.Size_+et.PtrBytes) } } memmove(p, oldPtr, lenmem) return slice{p, newLen, newcap} }
扩容规则:
如果新切片的长度大于旧切片容量的两倍,则直接将新切片的长度作为容量
容量小于256,直接两倍扩容
大于256,则循环扩容加上1/4容量和一个固定值192,
4 切片拷贝 4.1 赋值拷贝 1 2 s1 := []int {1 ,2 ,3 ,4 ,5 } s2 := s1[1 :]
这种通过赋值来拷贝的,两者会共用一个底层数组,意思就是两者指向的元素是相同的,即
4.2 copy或者slicecopy 如果不想要两个指向的同一个数组,则通过copy
或者slicecopy
来进行拷贝,两种拷贝方式都会通过 runtime.memmove
将整块内存的内容拷贝到目标的内存区域中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func slicecopy (toPtr unsafe.Pointer, toLen int , fromPtr unsafe.Pointer, fromLen int , width uintptr ) int { if fromLen == 0 || toLen == 0 { return 0 } n := fromLen if toLen < n { n = toLen } if width == 0 { return n } if size == 1 { *(*byte )(toPtr) = *(*byte )(fromPtr) } else { memmove(toPtr, fromPtr, size) } return n }
5 追加和删除 追加操作比较简单通过append()
即可实现
1 2 s := []int {2 ,3 ,4 } s = append (s,5 )
删除操作有如下几种情况
1 2 3 4 s := []int {0 ,1 ,2 ,3 ,4 } s = s[1 :] s = append (s[:2 ],s[3 :]...)