为什么在Go语言中使用append函数对切片进行追加操作会改变原始切片?

huangapple go评论79阅读模式
英文:

Why append slice in Go change the original slice?

问题

我有这段代码:

// 输入参数是 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 // 通过值复制切片

    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 的切片也发生了变化?

英文:

I have this code:

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

		fmt.Println(&quot;A: &quot;, A)
		fmt.Println(&quot;tempA: &quot;, 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 output:

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]

The variable A also changes while I just copy it by value into tempA. And also it is append() function, why the slice that used to append tempA also changed?

答案1

得分: 3

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

你可以将其想象为一个包含长度和指向数组某个元素的指针的结构体(不一定是第一个元素)。

当你给一个切片变量赋值时,实际上是在赋值切片头部所持有的长度和指针。所以 tempA 最终引用的是与 A 相同的底层数组。然后对其中任意一个进行索引操作都会访问相同的底层数组元素。

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

英文:

A slice variable of type []T, also called slice header, describes a contiguous section of a backing array. It is stored separately from the array data.

You can imagine it as a struct containing the length and a pointer to a certain item of the array (not necessarily the first one).

When you assign the value of a slice variable, you are actually assigning the length and pointer held by the slice header. So tempA ends up referencing the same backing array as A. Then indexing either will access the same underlying array item.

Recommended reading: https://blog.golang.org/slices

答案2

得分: 3

类型[]T是具有类型为T的元素的切片。那么,什么是切片呢?

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

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

假设你有一个切片:

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

那么它将指向一个后备数组[5]byte,你也可以在图像中看到。

现在,如果你执行:

y := x

以为这样会复制,但实际上不会。它只会创建一个新的切片描述符,指向与x指向的相同的后备数组。

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

copy内置函数将源切片的元素复制到目标切片中

使用copy函数,你还会得到一个新的后备数组,指向你的目标切片。

要了解更多关于切片的信息,请阅读这篇文章

英文:

The type []T is a slice with elements of type T. What is a slice, then?

> 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).

为什么在Go语言中使用append函数对切片进行追加操作会改变原始切片?

The rectangle on left is the slice descriptor. The descriptor has 3 fields i.e., a pointer to the array, length, and capacity of array segment it is pointing to. Now, the pointer is pointing to a backing array on the right that actually stores the elements.

Suppose, you have a slice:

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

Then it'd point to a backing array [5]byte as you can also see in the image.

Now, if you do:

y := x

thinking that would copy, but it wouldn't. It'd just create a new slice descriptor pointing to the same backing array that x was pointing to.

Hence, there's a built-in called copy which would help you copy (exactly what you want)

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

With copy you also get a new backing array that your destination site is pointing to.

To know more about slices, read this.

答案3

得分: 0

你需要使用copy函数:

package main
import "fmt"

func main() {
   a := []int{3, 4, 5, 3, 7}
   // 不好的做法
   b := a
   // 好的做法
   c := make([]int, len(a))
   copy(c, a)
   // 修改了b和a!
   b = append(b[:1], b[2:]...)
   // c保持不变,a被破坏了
   // [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

英文:

You need to utilize the copy function:

package main
import &quot;fmt&quot;

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

huangapple
  • 本文由 发表于 2021年6月19日 09:50:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/68042972.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定