【问题标题】:Why append slice in Go change the original slice?为什么在 Go 中追加 slice 会改变原始 slice?
【发布时间】:2021-09-03 15:39:25
【问题描述】:

我有这个代码:

// The input param is A := []int{3, 4, 5, 3, 7}
func someFunc(A []int) int {   
...
    ways := 0       
    i := 0
    for i < len(A) {
        if i+1 == len(A) || i == len(A) {
            fmt.Println("break")
            break
        }
        tempA := A // copy the slice by value

        fmt.Println("A: ", A)
        fmt.Println("tempA: ", A)
        fmt.Println()

        newArr = remove(tempA, i)

        if isAesthetic(newArr) {
            ways++
        }
        i++
    }
...
}

func remove(slice []int, s int) []int {
    return append(slice[:s], slice[s+1:]...)
}

Cosole 输出:

A:  [3 4 5 3 7]
tempA:  [3 4 5 3 7]

A:  [4 5 3 7 7]
tempA:  [4 5 3 7 7]

A:  [4 3 7 7 7]
tempA:  [4 3 7 7 7]

A:  [4 3 7 7 7]
tempA:  [4 3 7 7 7]

变量A 也发生了变化,而我只是将其按值复制到tempA 中。而且还是append()函数,为什么之前附加tempA的切片也变了?

【问题讨论】:

  • tempA := A 不按值复制切片;它使tempA指向与A相同的内容,所以修改tempA的内容也会修改A
  • @EliBendersky 这个geeksforgeeks.org/… 参考是一个骗局。那么如何让它按值复制呢?所以AtempA 改变时不会改变。
  • 切片是数组的视图。切片不是数组。分配数组会复制数组。分配切片会复制切片头而不是其内容。
  • @alramdein 请参阅slice tricks 中的复制部分。
  • Go 中的所有数据类型都有一个静态大小。如果它没有恒定数量的元素,则它不是数组。切片也是静态大小的,因为真实数据没有存储在值“内部”(因此是您的问题)。

标签: go append slice


【解决方案1】:

[]T 类型的切片变量,也称为切片头,描述了后备数组的连续部分。它与数组数据单独存储

你可以把它想象成一个结构体,包含长度和指向数组中某一项的指针(不一定是第一项)。

当您分配切片变量的值时,您实际上是在分配切片头所持有的长度和指针。所以tempA 最终引用了与A 相同的后备数组。然后索引任一将访问相同的底层数组项。

推荐阅读:https://blog.golang.org/slices

【讨论】:

    【解决方案2】:

    你需要使用copy函数:

    package main
    import "fmt"
    
    func main() {
       a := []int{3, 4, 5, 3, 7}
       // bad
       b := a
       // good
       c := make([]int, len(a))
       copy(c, a)
       // CHANGES b and a!
       b = append(b[:1], b[2:]...)
       // c stays the same, a is messed up
       // [3 5 3 7 7] [3 5 3 7] [3 4 5 3 7]
       fmt.Println(a, b, c)
    }
    

    https://golang.org/pkg/builtin#copy

    【讨论】:

    • 这样的结果和我想要的完全一样。我已经使用copy() 来解决这个问题,但我错过了我需要先用我想要复制的切片的相同 length 来初始化它的点。谢谢。
    【解决方案3】:

    []T 类型是 slice 类型的元素 T。那么slice 是什么?

    切片是数组段的描述符。它由指向数组的指针、段的长度和它的容量(段的最大长度)组成。

    左边的矩形是切片描述符。描述符有 3 个字段,即指向数组的指针、长度和它所指向的数组段的容量。现在,指针指向右侧实际存储元素的后备数组。

    假设,你有一个切片:

    x := make([]byte, 5, 5)
    

    然后它会指向一个支持数组[5]byte,您也可以在图片中看到。

    现在,如果你这样做:

    y := x
    

    认为这会复制,但它不会。它只会创建一个新的切片描述符,指向 x 指向的同一个支持数组。

    因此,有一个名为 copy 的内置函数可以帮助您复制(正是您想要的)

    The copy built-in function copies elements from a source slice into a destination slice.
    

    使用copy,您还可以获得目标站点指向的新后备数组。

    要了解有关切片的更多信息,请阅读this

    【讨论】:

    • 感谢您的详细解释。这个答案对我来说几乎是完美的,但有一点我想念其他用户已经回答了。谢谢。
    猜你喜欢
    • 2020-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-24
    • 2022-11-21
    • 2011-01-07
    • 2014-01-25
    • 2018-08-12
    相关资源
    最近更新 更多