【问题标题】:Golang: Problems when using append on sliceGolang:在切片上使用附加时出现问题
【发布时间】:2015-07-13 09:59:21
【问题描述】:

我正在使用 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

【问题讨论】:

标签: go slice


【解决方案1】:

这是有问题的代码:

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

您问为什么v 在两个Println() 语句之间发生变化。

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

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 个元素不会分配新的数组/切片,它将是“就地”完成。这个“就地”是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]

【讨论】:

  • 感谢@icza。我想我已经理解了这个问题。
猜你喜欢
  • 2016-05-25
  • 2019-01-02
  • 2013-12-10
  • 2020-12-09
  • 2019-11-30
  • 1970-01-01
  • 2016-12-24
  • 2021-07-14
  • 2017-08-11
相关资源
最近更新 更多