The following is the go languagetutorial column to introduce the underlying source code of Golang slice (Slice) to everyone, I hope it will be helpful to friends in need!
Before talking about slicing, let’s talk about the array. Two characteristics of the array
Go arrays are value types, and assignment and function parameter passing operations will copy the entire array data.
arr := [2]int{1,2}arr2 := arr fmt.Printf("%p %p",&arr ,&arr2)//切片slice1 := []int{1,2}slice2 := slice1 fmt.Printf("%p %p",slice1 ,slice2)
A slice is a reference to a continuous segment of an array, so a slice is a reference type. A slice is an array of variable length.
The data structure of Slice is defined as follows:
runtime/slice.go#L13
type slice struct { array unsafe.Pointer len int cap int}
src/runtime/slice.go#L83
func makeslice(et *_type, len, cap int) unsafe.Pointer { mem, overflow := math.MulUintptr(et.size, uintptr(cap)) .... return mallocgc(mem, et, true)}
The basic logic is to apply for a piece of memory based on capacity.
Expansion is when the length of the slice is greater than the capacity and the underlying array cannot fit in it
func growslice(et *_type, old slice, cap int) slice { ... // 如果新要扩容的容量比原来的容量还要小,直接报panic if cap < old.cap { panic(errorString("growslice: cap out of range")) } // 如果当前切片的大小为0,还调用了扩容方法,那么就新生成一个新的容量的切片返回 // []struct{} if et.size == 0 { return slice{unsafe.Pointer(&zerobase), old.len, cap} } newcap := old.cap doublecap := newcap + newcap //要扩容的容量大于2 *oldcap 新切片容量 = 该容量 if cap > doublecap { newcap = cap } else { // 旧容量 小于1024,新容量= 旧容量 * 2 也就是扩容1倍 if old.cap < 1024 { newcap = doublecap } else { // 扩容容量 = 旧容量 +旧容量*1/4 for 0 < newcap && newcap < cap { newcap += newcap / 4 } //溢出之后 新容量=要扩容的容量 if newcap <= 0 { newcap = cap } } } var overflow bool // 计算新的切片的容量,长度。 var lenmem, newlenmem, capmem uintptr .... var p unsafe.Pointer if et.ptrdata == 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(old.array), lenmem-et.size+et.ptrdata) } } //移动到p memmove(p, old.array, lenmem) //返回slice结构,让slice.array指向p return slice{p, old.len, newcap}}
arr := make([]int,1024) arr = append(arr,1) fmt.Println(len(arr),cap(arr))// 1025,1280 arr1 := make([]int,10) arr1 = append(arr1,1) fmt.Println(len(arr1),cap(arr1))//11 20
arr := []int{1,2,3,4} arr1 := arr[:2] //[1,2] arr1 = append(arr1,5) fmt.Println(arr[3]) //5 修改了底层数组 //例子2 arr3 := []int{1,2,3,4} arr4 := arr3[2:] arr4 = append(arr4,10)//扩容 不会影响arr3 fmt.Println(arr3)
src/runtime/slice.go#L247
//toPtr 目标地址 toLen目标长度 // width 元素大小 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 } //切片大小等于0 if width == 0 { return n } size := uintptr(n) * width //特殊处理 如果只有一个元素并且大小是1byte,那么指针直接转换即可 if size == 1 { *(*byte)(toPtr) = *(*byte)(fromPtr) } else { //从 fm.array 地址开始,拷贝到 to.array 地址之后 memmove(toPtr, fromPtr, size) } return n }
The above is the detailed content of Read the underlying source code of Golang Slice. For more information, please follow other related articles on the PHP Chinese website!