func growslice(et *_type, old slice, cap int) slice {
// 是否启用数据竞争监测
if raceenabled {
callerpc := getcallerpc()
racereadrangepc(old.array, uintptr(old.len*int(et.size)), callerpc, funcPC(growslice))
}
if msanenabled {
msanread(old.array, uintptr(old.len*int(et.size)))
}
// 如果扩容后的cap还小于扩容前的cap 直接panic
if cap < old.cap {
panic(errorString("growslice: cap out of range"))
}
// 如果当前slice大小为0还调用了扩容,则直接返回一个新slice
if et.size == 0 {
// append 没法创建一个nil指针的但是len不为0的切片
return slice{unsafe.Pointer(&zerobase), old.len, cap}
}
// 新容量的计算
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap { // 如果指定容量>2倍扩容前slice容量,则直接使用指定的容量
newcap = cap
} else { // 如果指定容量小于或者等于2倍当前slice容量
if old.len < 1024 { // 当扩容前slice长度<1024时,则直接扩容2倍
newcap = doublecap
} else { // 当扩容前的长度>=1024
// 检查是否存在越界,防止死循环;
for 0 < newcap && newcap < cap { // 元素个数>=1024,则扩容为1.25倍,但<cap
newcap += newcap / 4
}
if newcap <= 0 {
newcap = cap
}
}
}
// 计算对应的内存块大小(span)
var overflow bool
var lenmem, newlenmem, capmem uintptr
switch {
case et.size == 1:
lenmem = uintptr(old.len)
newlenmem = uintptr(cap)
capmem = roundupsize(uintptr(newcap))
overflow = uintptr(newcap) > maxAlloc
newcap = int(capmem)
case et.size == sys.PtrSize:
lenmem = uintptr(old.len) * sys.PtrSize
newlenmem = uintptr(cap) * sys.PtrSize
capmem = roundupsize(uintptr(newcap) * sys.PtrSize)
overflow = uintptr(newcap) > maxAlloc/sys.PtrSize
newcap = int(capmem / sys.PtrSize)
case isPowerOfTwo(et.size):
var shift uintptr
if sys.PtrSize == 8 {
shift = uintptr(sys.Ctz64(uint64(et.size))) & 63
} else {
shift = uintptr(sys.Ctz32(uint32(et.size))) & 31
}
lenmem = uintptr(old.len) << shift
newlenmem = uintptr(cap) << shift
capmem = roundupsize(uintptr(newcap) << shift)
overflow = uintptr(newcap) > (maxAlloc >> shift)
newcap = int(capmem >> shift)
default:
lenmem = uintptr(old.len) * et.size
newlenmem = uintptr(cap) * et.size
capmem, overflow = math.MulUintptr(et.size, uintptr(newcap))
capmem = roundupsize(capmem)
newcap = int(capmem / et.size)
}
// 如果所需要的内存超过最大可分配内存则panic
if overflow || capmem > maxAlloc {
panic(errorString("growslice: cap out of range"))
}
var p unsafe.Pointer
if et.ptrdata == 0 {
//分配一段新内存
p = mallocgc(capmem, nil, false)
memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem)
} else {
//如果元素是指针类型,申请一段新内存时需要置0,不然会被GC扫描,指针类型要考虑GC写屏障
p = mallocgc(capmem, et, true)
if lenmem > 0 && writeBarrier.enabled {
bulkBarrierPreWriteSrcOnly(uintptr(p), uintptr(old.array), lenmem-et.size+et.ptrdata)
}
}
// 把slice中的数据拷贝到新申请内存的低地址
memmove(p, old.array, lenmem)
// 返回新创建slice并设置cap为newcap
return slice{p, old.len, newcap}
}
拷贝操作
slicecopy
package main
func main() {
src := []int{ 1, 2, 3 }
tar := make([]int, 2)
copy(tar, src)
}
runtime.makeslicecopyruntime/slice.go
func makeslicecopy(et *_type, tolen int, fromlen int, from unsafe.Pointer) unsafe.Pointer {
// 计算需要复制的内存块大小
// 目标slice长度 > 来源slice长度,以来源长度为准
// 目标slice长度 <= 来源slice长度,以目标长度为准
var tomem, copymem uintptr
if uintptr(tolen) > uintptr(fromlen) { //目标slice长度>来源slice长度,已目标
// 目标slice内存,如果大于最大可分配内存,则panic
var overflow bool
tomem, overflow = math.MulUintptr(et.size, uintptr(tolen))
if overflow || tomem > maxAlloc || tolen < 0 {
panicmakeslicelen()
}
// 需要拷贝的内存为源目标内存
copymem = et.size * uintptr(fromlen)
} else {
// 需要拷贝的内存为目标内存
tomem = et.size * uintptr(tolen)
copymem = tomem
}
// 申请目标slice内存块
var to unsafe.Pointer
if et.ptrdata == 0 {
to = mallocgc(tomem, nil, false)
if copymem < tomem {
memclrNoHeapPointers(add(to, copymem), tomem-copymem)
}
} else {
to = mallocgc(tomem, et, true)
if copymem > 0 && writeBarrier.enabled {
bulkBarrierPreWriteSrcOnly(uintptr(to), uintptr(from), copymem)
}
}
// 数据竞争检测和支持与内存清理程序的互操作
if raceenabled {
callerpc := getcallerpc()
pc := funcPC(makeslicecopy)
racereadrangepc(from, copymem, callerpc, pc)
}
if msanenabled {
msanread(from, copymem)
}
// 复制内存块
memmove(to, from, copymem)
return to
}
删除操作
slice
func removeElem(src []int, toDel int) []int {
for i := 0; i < len(src); i++ {
if src[i] == toDel {
src = append(src[:i], src[i+1:]...)
i--
}
}
return src
}
func removeElem(src []int, toDel int) []int {
tar := src[:0]
for _, num := range src {
if num != toDel {
tar = append(tar, num)
}
}
return tar
}
func removeElem(src []int, toDel int) []int {
tar := make([]int, 0, len(src))
for _, num := range src {
if num != toDel {
tar = append(tar, num)
}
}
return tar
}