首页 >后端开发 >Golang >Go的append()函数什么时候创建一个新的切片?

Go的append()函数什么时候创建一个新的切片?

Barbara Streisand
Barbara Streisand原创
2024-10-30 12:31:501033浏览

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