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

Read the underlying source code of Golang Slice

藏色散人
Release: 2021-03-03 15:51:36
forward
2987 people have browsed it

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)
Copy after login

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}
Copy after login
  • 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)}
Copy after login

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 < 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}}
Copy after login
  • 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
Copy after login
  • 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)
Copy after login

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
}
Copy after login


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!

Related labels:
source:learnku.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template