Go切片覆盖

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

Go slice overwrite

问题

这是源代码:

package main

func main() {
    testSlice()
}

func testSlice() {
    slice := make([]int, 0)
    //slice = append(slice, 1)  ①
    //slice = append(slice, 1, 2)  ②
    //slice = append(slice, 1, 2, 3)  ③
    //slice = append(slice, 1, 2, 3, 4)  ④
    slice = append(slice, 1, 2, 3, 4, 5)  
    //slice = append(slice, 1, 2, 3, 4, 5, 6)  ⑥

    slice2 := append(slice, 1)
    slice3 := append(slice, 2)

    for _, i := range slice2 {
        print(i)
    }
    println()
    for _, i := range slice3 {
        print(i)
    }
}

期望输出:

123451
123452

实际输出:

123452
123452

除了 ⑤ 之外,①~⑥ 的输出都是符合预期的。但为什么 ⑤ 中 slice3 覆盖了 slice2

这个原因与指针或切片的调整大小有关吗?

英文:

Here is the source code:

package main

func main() {
    testSlice()
}

func testSlice() {
	slice := make([]int, 0)
	//slice = append(slice, 1)  ①
	//slice = append(slice, 1, 2)  ②
	//slice = append(slice, 1, 2, 3)  ③
	//slice = append(slice, 1, 2, 3, 4)  ④
	slice = append(slice, 1, 2, 3, 4, 5)  ⑤
	//slice = append(slice, 1, 2, 3, 4, 5, 6)  ⑥

	slice2 := append(slice, 1)
	slice3 := append(slice, 2)

	for _, i := range slice2 {
		print(i)
	}
	println()
	for _, i := range slice3 {
		print(i)
	}
}

Expected output:

123451
123452

Actual output:

123452
123452

The output of ①~⑥ except ⑤ is as expected. But why ⑤ slice3 overwrites slice2?

Is the reason related to pointer or slice resize?

答案1

得分: 1

请查看这个 Stack Overflow 回答,在阅读我回答的其余部分之前,它提供了关于切片(slice)的非常有帮助的解释。通过打印出实际的切片头部,可能更容易理解正在发生的情况。请参考以下示例代码(以及Go Playground):

package main

import (
	"fmt"
	"reflect"
	"unsafe"
)

func main() {
	testSlice()
}

func testSlice() {
	slice := make([]int, 0)
	slice = append(slice, 1, 2, 3, 4, 5)

	fmt.Printf("%+v\n", (*reflect.SliceHeader)(unsafe.Pointer(&slice)))
	slice2 := append(slice, 1)
	fmt.Printf("%+v\n", (*reflect.SliceHeader)(unsafe.Pointer(&slice2)))
	slice3 := append(slice, 2)
	fmt.Printf("%+v\n", (*reflect.SliceHeader)(unsafe.Pointer(&slice3)))

	for _, i := range slice2 {
		print(i)
	}
	println()
	for _, i := range slice3 {
		print(i)
	}
}

这将打印出类似以下的内容:

&{Data:824634441776 Len:5 Cap:6}
&{Data:824634441776 Len:6 Cap:6}
&{Data:824634441776 Len:6 Cap:6}

这表明所有变量 sliceslice2slice3 都指向相同的数据(Data 是指向切片第一个元素的指针),但切片头部本身是不同的。当执行 append 操作时,您正在修改所有变量共享的底层切片,并将新的切片头部存储到新的变量中。slice2slice3 的切片头部都指向相同的数据切片,因此当您执行 slice3append 操作时,您正在覆盖所有变量共享的底层切片中的第6个元素。

英文:

Check out this SO answer for a really helpful explanation of what a slice actually is before reading the rest of my answer. It might be easier to understand what's going on by printing out the actual slice headers. See the following example code (and go playground):

package main

import (
	"fmt"
	"reflect"
	"unsafe"
)

func main() {
	testSlice()
}

func testSlice() {
	slice := make([]int, 0)
	slice = append(slice, 1, 2, 3, 4, 5)

	fmt.Printf("%+v\n", (*reflect.SliceHeader)(unsafe.Pointer(&slice)))
	slice2 := append(slice, 1)
	fmt.Printf("%+v\n", (*reflect.SliceHeader)(unsafe.Pointer(&slice2)))
	slice3 := append(slice, 2)
	fmt.Printf("%+v\n", (*reflect.SliceHeader)(unsafe.Pointer(&slice3)))

	for _, i := range slice2 {
		print(i)
	}
	println()
	for _, i := range slice3 {
		print(i)
	}
}

This will print something like this:

&{Data:824634441776 Len:5 Cap:6}
&{Data:824634441776 Len:6 Cap:6}
&{Data:824634441776 Len:6 Cap:6}

This shows that all variables slice, slice2, and slice3 are pointing at the same data (Data which is a pointer to the first element of the slice) but the slice headers themselves are different. When you're performing your appends, you are modifying the underlying slice shared by all the variables, and storing new slice headers into new variables. The slice headers for slice2 and slice3 are looking at the same slice of data, so when you come along and perform your append for slice3, you're overwriting the 6th element in the underlying slice that is shared by all the variables.

huangapple
  • 本文由 发表于 2022年3月21日 11:26:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/71552505.html
匿名

发表评论

匿名网友

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

确定