为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
三国纷争 2021-09-13 10:29:42
根据内置的 api docs,当原始切片的容量不够大时, append() 将重新分配并复制到新的数组块。这是用于创建字母组合(在本例中为布尔值)的递归算法(的简化版本)。字母表的成员 (true, false) 被递归地添加到切片中,直到它的长度正确,然后通过通道发送。package mainimport (    "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 []boolfor _, ch := range []bool{true, false} {    newCombo = append(combo, ch)    AddOption(c, newCombo, length-1)}但是,根据文档, append() 应该返回一个新切片(cap(combo) 不够大)。根据这个答案,发送到 AddOption 的切片描述符应该是一个副本;这不是真的吗?据我所知,作为 AddOption() 的第二个参数发送的值要么是指向切片描述符的指针,要么 append() 不返回新切片。
查看完整描述

3 回答

?
倚天杖

TA贡献1828条经验 获得超3个赞

您将切片、数据类型与实际表示混淆了。切片描述符由一对 int 组成,一个用于 len,一个用于 cap,以及一个指向底层数据的指针。


因此, append 返回的确实是一个新切片,而传递给 add 选项的确实是切片描述符的副本。但是由于描述符有一个指向数据的指针,所以指针值(指向底层数据的地址)是相同的。


编辑:这是一个代码片段来说明我的观点:


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)

}

如果你运行这个,你会得到:


[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]


查看完整回答
反对 回复 2021-09-13
?
素胚勾勒不出你

TA贡献1827条经验 获得超9个赞

当append()创建一个新切片时,它不会创建一个只比之前的切片大一个的切片。它实际上创建了一个比前一个大几个元素的切片。看看这个代码:


package main


import "fmt"


func main() {

    var sl []bool


    for i := 0; i < 100; i++ {

        sl = append(sl, true)

        fmt.Println(cap(sl))

    }

}

Playground


如果您运行此代码,您会看到容量最初在每次分配时翻倍;对于更大的切片大小,这个策略当然会改变。


查看完整回答
反对 回复 2021-09-13
  • 3 回答
  • 0 关注
  • 268 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信