首頁  >  文章  >  後端開發  >  Go的append()函數什麼時候會建立一個新的切片?

Go的append()函數什麼時候會建立一個新的切片?

Barbara Streisand
Barbara Streisand原創
2024-10-30 12:31:50965瀏覽

When Does Go's append() Function Create a New Slice?

Go 的 Append() 何時會建立新的切片?

Go 語言的append() 函數用於擴展現有的切片。根據內建 API 文檔,當原始切片容量不足時,append() 可能會建立一個容量更大的新切片。

但是,在遞歸演算法的上下文中考慮時,這種行為會引發問題。特別是,以下演算法產生字母表的組合:

<code class="go">package main

import (
    "fmt"
)

func AddOption(c chan []bool, combo []bool, length int) {
    if length == 0 {
        fmt.Println(combo, "!")
        c <- combo
        return
    }
    var newCombo []bool
    for _, ch := range []bool{true, false} {
        newCombo = append(combo, ch)
        AddOption(c, newCombo, length-1)
    }
}

func main() {
    c := make(chan []bool)
    go func(c chan []bool) {
        defer close(c)
        AddOption(c, []bool{}, 4)
    }(c)
    for combination := range c {
        fmt.Println(combination)
    }
}</code>

在此程式碼中,AddOption 函數遞歸地將字母表的成員加入切片,並透過通道發送結果。然而,觀察表明發送到通道的切片在發送後會被修改。

矛盾的出現是因為文件表明append()應該回傳一個新的切片,但程式碼中的行為暗示了其他情況。本文研究了append()的底層機制,並闡明了它何時創建新切片。

理解切片表示

要理解append()的行為,它是對於理解切片的內部表示至關重要。儘管切片具有獨立的外觀,但它並不是一個獨立的資料結構。相反,它由指向實際資料的底層數組的描述符組成。

切片描述符由三個組成部分組成:

  1. 長度:切片中當前元素的數量.
  2. 容量:底層數組可以容納的元素數量。
  3. 資料指標:指向底層陣列第一個元素的指標。

Append() 的回傳值

使用append() 時,函數會建立一個具有自己的長度、容量和資料指標的新切片描述符。這與文件一致,文件指出append()「重新分配[s]並複製[ies]到新的數組塊。」

但是,這提出了另一個問題:為什麼要對切片進行更改描述符發送到通道後會保留在原始切片中嗎?

理解共享引用

解決這個問題的關鍵是理解資料指標在切片描述符。此指標不會建立底層資料的副本;它指向與原始切片相同的資料。

因此,當對切片使用append()時,雖然它創建了新的切片描述符,但資料指標保持不變。這意味著對任一切片描述符的元素所做的任何修改都將反映在兩個切片中,無論修改發生在何處。

示範

來說明此概念,考慮以下程式碼片段:

<code class="go">package main

import "fmt"

func main() {
    s := make([]int, 0, 5)
    s = append(s, []int{1, 2, 3, 4}...)

    a := append(s, 5)
    fmt.Println(a)

    b := append(s, 6)
    fmt.Println(b)
    fmt.Println(a)
}</code>

執行此程式碼時,它輸出:

<code class="go">package main

import (
    "fmt"
)

func AddOption(c chan []bool, combo []bool, length int) {
    if length == 0 {
        fmt.Println(combo, "!")
        c <- combo
        return
    }
    var newCombo []bool
    for _, ch := range []bool{true, false} {
        newCombo = append(combo, ch)
        AddOption(c, newCombo, length-1)
    }
}

func main() {
    c := make(chan []bool)
    go func(c chan []bool) {
        defer close(c)
        AddOption(c, []bool{}, 4)
    }(c)
    for combination := range c {
        fmt.Println(combination)
    }
}</code>

在此範例中,切片 a 和 b 最初共享相同的基礎資料。但是,當為 b 指派新值時,將建立一個新的基礎資料數組,並且更新 b 的資料指標以指向它。由於a仍然引用相同的資料指針,所以它繼續存取舊的資料數組。

透過修改切片容量,可以證明當容量足以避免重新分配時,切片確實共享底層資料。

結論

Go 的append() 函數分配一個新的切片描述符,但維護對原始資料數組的引用。這意味著在遞歸演算法中對切片的修改將在共享相同資料引用的所有切片中可見。理解這種行為對於在 Go 中有效地使用切片至關重要。

以上是Go的append()函數什麼時候會建立一個新的切片?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn