当前位置: 首页 > 面试题库 >

Go何时分配新的支持数组进行切片?

白嘉志
2023-03-14
问题内容

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

如果s的后备数组太小而无法容纳所有给定值,则会分配更大的数组。返回的切片将指向新分配的数组。
来源-高朗之旅

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

尝试去游乐场

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指向相同的基础数组,因为它们都是相同长度的切片

但是如果我将相同的代码用于另一片切片:

尝试去游乐场

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他们再次同长度的片,但他们不应该指向了同一个后备数组。

为什么这种行为异常?Go何时确切决定将新的支持数组分配给切片?


问题答案:

答案很简单: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,因此将分配一个新的支持数组(其容量将大于len +
3,考虑到未来的增长,但其长度将是2 + 3 = 5)。

解释你的第一个例子

在第一个示例中,您声明一个切片变量,其长度和容量为0。

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

但是这次您将结果切片存储在中b,而不是中a。因此,如果对进行第3次追加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切片,因此它们第一元件的地址将是相同的。这就是您所看到的。

解释第二个例子

再次创建一个空切片(len = 0,cap = 0)。

然后执行第一个附加操作:2个元素:

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

这将分配一个新的数组,其长度为2,因此切片的长度和容量均为2。

然后执行第二次追加:

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

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

然后,执行第三个附加操作,但是要附加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它们具有不同的支持数组,并且将附加到与“另一个”切片共享支持数组的任何切片都不会(无法)更改此“另一个”切片。因此,结果是您d两次看到相同的地址,而看到的是不同的地址e



 类似资料:
  • 切片包含三个组件:长度、容量和指向底层数组的指针。 当我们尝试将

  • 我有一个光谱仪的波长和吸光度输入文件。在这个文件中,数据被记录并作为数据帧的最后两列添加。柱需要指定测量特定吸光度(=数据)的波长。 我希望有一个数据框架,使我的分析更容易一点。诸如此类: 我知道,凭借我相当基本的python技能,我可能会将每个波长数据对存储为元组列表,并使一些复杂的排序魔法发生。但是自从我试图了解更多关于熊猫模块的信息以来,我一直在想我是否能更轻松地解决这个问题。然而,虽然我发

  • 问题内容: 我正在尝试对Go中的整数切片进行反向排序。 如何对它进行排序,使其从最高到最低?所以[25 5 4 3 1] 我已经试过了 资料来源:http : //golang.org/pkg/sort/#Reverse 但是,我得到下面的错误 问题答案: sort.Ints是对几个int进行排序的便捷函数。通常,如果要对某些内容进行排序和排序,则需要实现sort.Interface接口.Reve

  • Go内置了对JSON数据的编码和解码,这些数据的类型包括内置数据类型和自定义数据类型。 package main import "encoding/json" import "fmt" import "os" // 我们使用两个结构体来演示自定义数据类型的JSON数据编码和解码。 type Response1 struct { Page int Fruits []string

  • 若要切换作业的分支,就要进行checkout操作。进行checkout时,git会从工作树还原向目标分支提交的修改内容。checkout之后的提交记录将被追加到目标分支。 HEAD HEAD指向的是现在使用中的分支的最后一次更新。通常默认指向master分支的最后一次更新。通过移动HEAD,就可以变更使用的分支。 Note 提交时使用~(tilde)和^(caret)就可以指定某个提交的相对位置。

  • 目前git正在做我的头,我不能想出以下最好的解决方案。 有两个分支,一个叫master,一个叫mobiledevicesupport。我希望将mobiledevicesupport保持为一个连续分支,只要mobiledevicesupport稳定,它就会与主分支合并/同步。这将把mobiledevicesupport中的更改合并到master中,但也会把master中的所有更改都合并到mobile