Golang Slice の基礎となるソースコードを読む

藏色散人
リリース: 2021-03-03 15:51:36
転載
2845 人が閲覧しました

以下は、Golang スライス (Slice) の基礎となるソース コードを皆さんに紹介する go 言語 チュートリアル コラムです。友人の参考になれば幸いです。必要!

Golang Slice の基礎となるソースコードを読む

配列

スライスについて話す前に、配列について話しましょう。配列の 2 つの特徴

  • 連続したメモリアドレス、各要素が連続している
  • 要素は同じ型で、要素数は固定です

Go 配列は値型であり、代入および関数パラメーターの受け渡し操作により配列データ全体がコピーされます。

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)
ログイン後にコピー

スライス

スライスは配列の連続セグメントへの参照であるため、スライスは参照型であり、スライスは可変長の配列です。

スライスのデータ構造は次のように定義されます:

runtime/slice.go#L13

type slice struct {
    array unsafe.Pointer    len   int
    cap   int}
ログイン後にコピー
  • array は基になる配列のアドレスです
  • len スライスの長さ
  • cap スライスの容量

スライスの作成

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)}
ログイン後にコピー

基本的なロジックは、容量に基づいてメモリを適用することです。

#スライス拡張

拡張とは、スライスの長さが容量を超え、基礎となる配列がそれに収まらない場合のことです。

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}}
ログイン後にコピー
    新しいアプリケーション容量の上限が古い容量 (oldcap) の 2 倍を超える場合、拡張される容量 (newcap) = 新しい適用容量の上限
  • 古い容量 (oldcap) 値がオーバーフローした場合、拡張する容量 =新しく適用された容量
  •   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
}
ログイン後にコピー

以上がGolang Slice の基礎となるソースコードを読むの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:learnku.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!