Golang:在切片上使用附加时出现问题

Golang: Problems when using append on slice

我正在使用 golang。这是我的代码:

func main() {
    quanPailie([]int{1, 2})
}

func quanPailie(nums []int) [][]int {
    COUNT := len(nums)

    //only one item
    if COUNT == 1 {
        return [][]int{nums}
    }

    insertItem(quanPailie(nums[:COUNT-1]), nums[COUNT-1])
    return [][]int{}
}

func insertItem(res [][]int, insertNum int) {
    fmt.Println("insertItem,res:", res, "insertNum", insertNum) //insertItem,res: [[1]] insertNum 2

    for _, v := range res {
        for i := 0; i < len(v); i++ {
            fmt.Println("===before,v:", v)
            c := append(v[:i], append([]int{insertNum}, v[i:]...)...)
            fmt.Println("===after,v:", v)

            fmt.Println("ccc", c)

        }
    }
}

让我很困惑的是输出:

===before,v: [1]
===after,v: [2]

为什么 v 的值发生了变化?希望可以有人帮帮我。非常感谢。

去游乐场:https://play.golang.org/p/wITYsGpX7U

编辑:
感谢icza的大力帮助,我想我已经理解了这个问题。
并且,这里有一个简单的代码来说明这个问题。

func test1() {
    nums := []int{1, 2, 3}
    _ = append(nums[:2], 4)
    fmt.Println("test1:", nums)

    //nums changes because the cap is big enought, the original array is modified.

}

func test2() {
    nums := []int{1, 2, 3}
    c := append(nums[:2], []int{4, 5, 6}...)
    fmt.Println("test2:", nums)
    fmt.Println("cc:", c)

    //nums dont't change because the cap isn't big enought.
    //a new array is allocated while the nums still points to the old array.
    //Of course, the return value of append points to the new array.
}

去游乐场:https://play.golang.org/p/jBNFsCqUn3

这是有问题的代码:

fmt.Println("===before,v:", v)
c := append(v[:i], append([]int{insertNum}, v[i:]...)...)
fmt.Println("===after,v:", v)

你问为什么 v 在 2 个 Println() 语句之间变化。

因为您使用的是内置 append() 函数,引用其文档:

The append built-in function appends elements to the end of a slice. If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated. Append returns the updated slice.

因此,如果您附加到的切片有足够的空间(容量)来容纳您要附加的元素,则不会分配新切片,而是重新切片目标切片(这将使用相同的底层array) 和 append 将发生在其中。

让我们检查容量:

fmt.Println("===before,v:", v, cap(v))
c := append(v[:i], append([]int{insertNum}, v[i:]...)...)
fmt.Println("===after,v:", v, cap(v))

输出:

===before,v: [1] 2
===after,v: [2] 2

v 切片的容量为 2。当 for 循环开始时,i=0v[:i]v[:0],它是一个空切片(但容量为 2),因此附加 1 或 2 个元素不会分配一个新的 array/slice,它将完成 "in place"。 "in place" 是 v 的第 0 个元素,因为 v[:i]v[0:i] 的 shorthand。因此,元素将从共享的基础数组中的 v[0] 开始追加,因此由 v[0] 表示的元素将发生变化。

请注意,对切片进行切片会生成与原始切片共享其底层支持数组的切片(不会复制元素)。

如果您想避免这种情况,请使用或分配一个新切片,copy 原始内容并附加到新切片,例如:

src := []int{1, 2}
c := make([]int, len(src))
copy(c, src)
// Append something:
c = append(c, 3, 4)

fmt.Println(src) // [1 2] - src doesn't change
fmt.Println(c)   // [1 2 3 4]