Maison > développement back-end > Golang > le corps du texte

Lire le code source sous-jacent de Golang Slice

藏色散人
Libérer: 2021-03-03 15:51:36
avant
2928 Les gens l'ont consulté

Ce qui suit est une introduction au code source sous-jacent de Golang Slice (Slice) de la colonne du didacticiel go langage . J'espère que cela sera utile aux amis dans le besoin. !

Lire le code source sous-jacent de Golang Slice

Array

Avant de parler de découpage, parlons du tableau. Deux caractéristiques des tableaux

  • Une adresse mémoire continue, chaque élément est continu
  • Les éléments sont du même type, et le nombre d'éléments est fixe
Go sont des types de valeur, et les opérations de passage de paramètres d'affectation et de fonction copieront l'intégralité des données du tableau.

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)
Copier après la connexion

Slice

Une tranche est une référence à un fragment continu d'un tableau, donc une tranche est un type de référence. Une tranche est un tableau de longueur variable.

La structure des données de Slice est définie comme suit :

runtime/slice.go#L13

type slice struct {
    array unsafe.Pointer    len   int
    cap   int}
Copier après la connexion
    array est l'adresse du tableau sous-jacent
  • longueur de la tranche
  • capacité maximale de la tranche

Créer une tranche

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)}
Copier après la connexion
La logique de base est de demander un morceau de mémoire en fonction de la capacité.

Extension de tranche

L'expansion se produit lorsque la longueur de la tranche est supérieure à la capacité et que le tableau sous-jacent ne peut pas y tenir

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}}
Copier après la connexion
    Nouveau plafond de capacité d'application, s'il est supérieur à 2 fois l'ancienne capacité (oldcap), la capacité à étendre (newcap) = le plafond de capacité nouvellement appliqué
  • Si l'ancienne capacité (oldcap) Si la valeur déborde, la capacité à étendre = la capacité nouvellement appliquée
  •   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
    Copier après la connexion
    Remarques : partage de tranches Le tableau sous-jacent, donc lorsque la tranche est attribuée, la modification de la tranche entraînera une modification du tableau sous-jacent, ce qui entraînera un 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)
    Copier après la connexion
Copie de tranche

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
}
Copier après la connexion


Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:learnku.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal