gpt4 book ai didi

go - Go什么时候分配一个新的后备数组给slice?

转载 作者:IT王子 更新时间:2023-10-29 01:51:48 26 4
gpt4 key购买 nike

在阅读 Go slice 时,我在 append 方法的上下文中遇到了这种行为

If the backing array of s is too small to fit all the given values a bigger array will be allocated. The returned slice will point to the newly allocated array.
Source - Golang Tour

为了理解这一点,我编写了以下代码:

<子> Try on the Go Playground

func makeSlices() {
var a []int;

a = append(a, 0)

b := append(a, 1)
printSlice("b", b)

c := append(a, 2)
printSlice("b", b)
printSlice("c", c)

}

func printSlice(name string, s []int) {
fmt.Printf("var=%v len=%d cap=%d first_address=%v %v\n", name, len(s), cap(s), &s[0], s)
}

输出:

var=b len=2 cap=2 first_address=0x414020 [0 1]
var=b len=2 cap=2 first_address=0x414020 [0 2]
var=c len=2 cap=2 first_address=0x414020 [0 2]

我希望 bc 指向相同的底层数组,因为它们都是相同长度的 slice


但是如果我要为另一个长度的 slice 改变相同的代码:

<子> Try on the Go Playground

func makeSlices() {
var a []int;

a = append(a, 0, 9)

d := append(a, 1, 2)
printSlice("d", d)

e := append(a, 3, 4)
printSlice("d", d)
printSlice("e", e)
}

输出:

var=d len=5 cap=8 first_address=0x450020 [0 0 9 1 2]
var=d len=5 cap=8 first_address=0x450020 [0 0 9 1 2]
var=e len=5 cap=8 first_address=0x450040 [0 0 9 3 4]

在这种情况下,de 应该指向相同的后备数组,因为它们又是相同长度的 slice ,但它们不是。


为什么会出现这种行为异常? Go 到底什么时候决定为 slice 分配一个新的支持数组?

最佳答案

答案很简单:如果要追加的元素不适合当前容量,append() 会分配一个新的后备数组(并复制当前内容)。正式地:

if len(s) + len(newElements) > cap(s) {
// Allocate new backing array
// copy content (s) over to new array
} else {
// Just resize existing slice
}
// append (copy) newElements

例如,如果 len=2,cap=4,您可以附加 2 个元素,无需分配。

如果len=2,cap=4,追加3个元素,则len+3 > cap,所以会分配一个新的backing array(容量会大于len+3,考虑到 future 的增长,但是它的长度将是 2+3=5)。

解释你的第一个例子

在您的第一个示例中,您声明了一个长度和容量为 0 的 slice 变量。

var a []int
fmt.Println(len(a), cap(a)) // Prints 0 0

当你第一次追加时,一个新的数组将被分配:

a = append(a, 0)
fmt.Println(len(a), cap(a)) // Prints 1 2

当你做另一个追加时,它适合容量,所以没有分配:

fmt.Println(len(a), cap(a)) // Prints 1 2
b := append(a, 1)
fmt.Println(len(b), cap(b)) // Prints 2 2

但是这次你将结果 slice 存储在 b 中,而不是 a 中。因此,如果您对 a 进行第三次追加,它仍然具有 length=1 和 cap=2,因此将另一个元素追加到 a 将不需要分配:

fmt.Println(len(a), cap(a)) // Prints 1 2
c := append(a, 2)
fmt.Println(len(c), cap(c)) // Prints 2 2

因此除了第一个追加之外,所有其他追加不需要分配,因此第一个分配的后备数组用于所有 abc slice ,因此它们第一个元素的地址将是相同的。这就是您所看到的。

解释你的第二个例子

您再次创建一个空 slice (len=0,cap=0)。

然后你做第一个追加:2个元素:

a = append(a, 0, 9)
fmt.Println(len(a), cap(a)) // Prints 2 2

这会分配一个长度为 2 的新数组,因此 slice 的长度和容量都将为 2。

然后你做你的第二次追加:

d := append(a, 1, 2)
fmt.Println(len(d), cap(d)) // Prints 4 4

由于没有空间容纳更多元素,因此分配了一个新数组。但是您将指向这个新数组的 slice 存储在 d 中,而不是 a 中。 a 仍然指向旧数组。

然后您进行第三次追加,但是添加到 a(指向旧数组):

fmt.Println(len(a), cap(a)) // Prints 2 2
e := append(a, 3, 4)
fmt.Println(len(e), cap(e)) // Prints 4 4

同样,a 的数组无法容纳更多元素,因此分配了一个新数组,您将其存储在 e 中。

所以 de 有不同的后备数组,并且附加到任何与“另一个” slice 共享后备数组的 slice 不会(不能)改变这个“另一个” slice 。所以结果是您两次看到 d 的相同地址,以及 e 的不同地址。

关于go - Go什么时候分配一个新的后备数组给slice?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55041641/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com