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

时间:2015-07-13 09:59:22

标签: go 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

1 个答案:

答案 0 :(得分:3)

这是有问题的代码:

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

您问为什么vPrintln()个语句之间发生了变化。

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

  

追加内置函数将元素附加到切片的末尾。 如果有足够的容量,则会复制目标以容纳新元素。如果没有,则会分配新的基础数组。 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个元素不会分配一个新的数组/切片,它将完成&#34;到位&#34;。这个&#34;到位&#34;是v的第0个元素,因为v[:i]v[0:i]的简写。因此,元素将从共享的基础数组中的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]