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

根据内置的 api docs,当原始切片的容量不够大时, append() 将重新分配并复制到新的数组块。


这是用于创建字母组合(在本例中为布尔值)的递归算法(的简化版本)。字母表的成员 (true, false) 被递归地添加到切片中,直到它的长度正确,然后通过通道发送。


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)

    }

}

这是此代码的操场链接。在输出中:


[true true true true] !

[true true true false] !

[true true true false]

[true true true false]

[true true false true] !

[true true false false] !

[true true false false]

[true true false false]

[true false true true] !

[true false true false] !

[true false true false]

[true false true false]

[true false false true] !

[true false false false] !

[true false false false]

[true false false false]

[false true true true] !

[false true true false] !

[false true true false]

[false true true false]

[false true false true] !

[false true false false] !

[false true false false]

[false true false false]

[false false true true] !

[false false true false] !

[false false true false]

[false false true false]

[false false false true] !

[false false false false] !

[false false false false]

[false false false false]

以感叹号结尾的行是从 AddOption 发送到通道的行。那些没有出现在另一侧(即在 main() 中)。很明显,通过通道发送的切片在发送后发生了变化。


由于AddOption在发送切片后立即返回,因此修改必须来自代码块


var newCombo []bool

for _, ch := range []bool{true, false} {

    newCombo = append(combo, ch)

    AddOption(c, newCombo, length-1)

}

但是,根据文档, append() 应该返回一个新切片(cap(combo) 不够大)。根据这个答案,发送到 AddOption 的切片描述符应该是一个副本;这不是真的吗?据我所知,作为 AddOption() 的第二个参数发送的值要么是指向切片描述符的指针,要么 append() 不返回新切片。


三国纷争
浏览 269回答 3
3回答

倚天杖

您将切片、数据类型与实际表示混淆了。切片描述符由一对 int 组成,一个用于 len,一个用于 cap,以及一个指向底层数据的指针。因此, append 返回的确实是一个新切片,而传递给 add 选项的确实是切片描述符的副本。但是由于描述符有一个指向数据的指针,所以指针值(指向底层数据的地址)是相同的。编辑:这是一个代码片段来说明我的观点:package mainimport "fmt"func main() {&nbsp; &nbsp; s := make([]int, 0, 5)&nbsp; &nbsp; s = append(s, []int{1, 2, 3, 4}...)&nbsp; &nbsp; a := append(s, 5)&nbsp; &nbsp; fmt.Println(a)&nbsp; &nbsp; b := append(s, 6)&nbsp; &nbsp; fmt.Println(b)&nbsp; &nbsp; fmt.Println(a)}如果你运行这个,你会得到:[1 2 3 4 5][1 2 3 4 6][1 2 3 4 6]因为既然s仍然有能力,都a和b共享相同的数据PTR。如果将容量更改为 4,则会打印:[1 2 3 4 5][1 2 3 4 6][1 2 3 4 5]

素胚勾勒不出你

当append()创建一个新切片时,它不会创建一个只比之前的切片大一个的切片。它实际上创建了一个比前一个大几个元素的切片。看看这个代码:package mainimport "fmt"func main() {&nbsp; &nbsp; var sl []bool&nbsp; &nbsp; for i := 0; i < 100; i++ {&nbsp; &nbsp; &nbsp; &nbsp; sl = append(sl, true)&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(cap(sl))&nbsp; &nbsp; }}Playground如果您运行此代码,您会看到容量最初在每次分配时翻倍;对于更大的切片大小,这个策略当然会改变。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go