Home  >  Article  >  Backend Development  >  Read the underlying source code of Golang Slice

Read the underlying source code of Golang Slice

藏色散人
藏色散人forward
2021-03-03 15:51:362891browse

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!

Read the underlying source code of Golang Slice

Array

Before talking about slicing, let’s talk about the array. Two characteristics of the array

  • A continuous memory address, each element is continuous
  • The elements are of the same type, and the number of elements is fixed

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)

Slice

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}
  • array is the address of the underlying array
  • len slice The length
  • cap The capacity of the slice

Create the slice

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.

Slice expansion

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 6fcbe6a181235a2d91480a84b8bdbb32 doublecap {
        newcap = cap
    } else {
    // 旧容量 小于1024,新容量= 旧容量 * 2 也就是扩容1倍
        if old.cap c9221202ba118f4c45c4e2fcde42ff0d 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}}
  • New application capacity cap, if it is greater than 2 times the old capacity (oldcap), the capacity to be expanded (newcap) = the new applied capacity cap
  • If the old capacity (oldcap)
  • If the value overflows, the capacity to be expanded = the newly applied capacity
  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
  • Notes: Slice sharing The underlying array, so when the slice is assigned, modifying the slice will cause the underlying array to change, resulting in BUG
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)

Slice copy

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!

Statement:
This article is reproduced at:learnku.com. If there is any infringement, please contact admin@php.cn delete