当给定 slice 的容量不足时,Go的append()函数仅分配新的 slice 数据(另请参见:https://stackoverflow.com/a/28143457/802833)。这可能导致意外行为(至少对我来说是golang新手):

package main

import (
    "fmt"
)

func main() {

    a1 := make([][]int, 3)
    a2 := make([][]int, 3)
    b := [][]int{{1, 1, 1}, {2, 2, 2}, {3, 3, 3}}
    common1 := make([]int, 0)
    common2 := make([]int, 0, 12) // provide sufficient capacity
    common1 = append(common1, []int{10, 20}...)
    common2 = append(common2, []int{10, 20}...)

    idx := 0
    for _, k := range b {
        a1[idx] = append(common1, k...) // new slice is allocated
        a2[idx] = append(common2, k...) // no allocation
        idx++
    }

    fmt.Println(a1)
    fmt.Println(a2) // surprise!!!
}
输出:

[[10 20 1 1 1] [10 20 2 2 2] [10 20 3 3 3]]
[[10 20 3 3 3] [10 20 3 3 3] [10 20 3 3 3]]

https://play.golang.org/p/8PEqFxAsMt
那么,在Go中强制分配新的 slice 数据或更准确地确保append()的slice参数保持不变的(惯性的)方法又是什么呢?

最佳答案

您可能对 slice 在Go中的工作方式保持错误的认识。

将元素追加到 slice 时,对append()的调用将返回一个新的 slice 。如果没有发生重新分配,则两个分片值(即您在其上调用append()的值和它返回的值)共享相同的后备数组,但它们的长度将不同。观察:

package main

import "fmt"

func main() {
    a := make([]int, 0, 10)
    b := append(a, 1, 2, 3)
    c := append(a, 4, 3, 2)
    fmt.Printf("a=%#v\nb=%#v\nc=%#v\n", a, b, c)
}

输出:
a=[]int{}
b=[]int{4, 3, 2}
c=[]int{4, 3, 2}

因此,len(a) == 0len(b) == 3len(c) == 3以及对append()的第二次调用会写出第一个调用的内容,因为所有 slice 都共享相同的基础数组。

关于支持数组的重新分配,the spec很清楚:

如果s的容量不足以容纳其他值,则append分配一个新的,足够大的基础数组,该数组适合现有slice元素和其他值。否则,append将重复使用基础数组。

由此得出:

如果附加的分片的容量足够,
  • append()永远不会复制基础存储。
  • 如果没有足够的容量,将重新分配阵列。

  • 也就是说,给定要向其附加s元素的片段N,如果cap(s) - len(s) ≥ N不会进行重新分配。

    因此,我怀疑您的问题不是与意外的重新分配结果有关,而是与Go中实现的 slice 概念有关。要吸收的代码思想是append()返回结果 slice 值,除非您完全了解其影响,否则应该在调用后使用该值。

    我建议从this开始以充分理解它们。

    关于go - Golang:追加或不分配 slice ,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/33783281/

    10-12 12:28
    查看更多