为什么在 Go 中追加切片会改变原来的切片?

Why append slice in Go change the original slice?

我有这个代码:

// 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:]...)
}

控制台输出:

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的slice也变了?

[]T 类型的切片变量,也称为切片 header,描述了支持数组的连续部分。它与数组数据分开存储

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

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

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

您需要使用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

类型 []Tslice,元素类型为 T。那么什么是slice呢?

A slice is a descriptor of an array segment. It consists of a pointer to the array, the length of the segment, and its capacity (the maximum length of the segment).

左边的矩形是切片描述符。描述符有 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