循环内的切片似乎保留了前一个/最后一个引用(取决于切片的长度)

2023-12-10

这看起来很奇怪,循环中有一个局部变量slice为每个循环分配新值,我将该切片附加到全局sliceWrappers。循环完成后,全局切片内的所有值仅包含对该局部切片变量上设置的最后一个值的引用。

Code:

package main

import (
    "fmt"
    "strconv"
)

func main() {
    var sliceWrappers [][]string
    initialSlice := append([]string{}, "hi")
    initialSlice = append(initialSlice, "there")
    
    // Remove this line and it works fine
    initialSlice = append(initialSlice, "I'm")
    
    for i := 0; i < 2; i++ {
        slice := append(initialSlice, strconv.Itoa(i))
        fmt.Printf("Slice Value : %+v, Initial Value : %+v\n", slice, initialSlice)
        sliceWrappers = append(sliceWrappers, slice)
    }

    for _, sliceWrapper := range sliceWrappers {
        fmt.Printf("%+v\n", sliceWrapper)
    }
}

实际输出:

Slice Value : [hi there I'm 0], Initial Value : [hi there I'm]
Slice Value : [hi there I'm 1], Initial Value : [hi there I'm]
[hi there I'm 1]
[hi there I'm 1]

预期输出:

Slice Value : [hi there I'm 0], Initial Value : [hi there I'm]
Slice Value : [hi there I'm 1], Initial Value : [hi there I'm]
[hi there I'm 0]  <------ This is not happening
[hi there I'm 1]

如果我删除initialSlice = append(initialSlice, "I'm")线,然后就可以完美运行了。

Slice Value : [hi there 0], Initial Value : [hi there]
Slice Value : [hi there 1], Initial Value : [hi there]
[hi there 0]  <------ Works Perfectly
[hi there 1]

我相信这与append

append 内置函数将元素追加到切片的末尾。 如果有足够的容量,目的地将被重新分割为 容纳新元素。

如果上述情况是造成这种情况的原因,那么不应值initialSlice循环内打印的内容也与slice?

操场 - https://play.golang.org/p/b3SDGoA2Lzv

PS:不幸的是,我为我的代码编写了仅 3 层嵌套的测试用例,并且顺利通过。我现在必须照顾copy对于循环内的切片。


切片基于指向数组的指针、长度和容量。你把slice in sliceWrappers在第一次迭代时(因此它包含指向数组的指针)。在第二次迭代中,append(initialSlice, strconv.Itoa(i))调用会更改同一数组中的值,因为内存位置未更改。两者都指向该数组slice在第一次和第二次迭代中,所以两个切片最终都在sliceWrappers指向相同的数据。

您可以通过在将数据添加到之前将数据复制到新切片来避免这种情况sliceWrappers:

    for i := 0; i < 2; i++ {
        slice := append(initialSlice, strconv.Itoa(i))
        fmt.Printf("Slice Value : %+v, Initial Value : %+v\n", slice, initialSlice)
        copiedSlice := make([]string, len(slice))
        copy(copiedSlice, slice)
        sliceWrappers = append(sliceWrappers, copiedSlice)
    }

这给出了预期的输出:

Slice Value : [hi there I'm 0], Initial Value : [hi there I'm]
Slice Value : [hi there I'm 1], Initial Value : [hi there I'm]
[hi there I'm 0]
[hi there I'm 1]

至于去掉线initialSlice = append(initialSlice, "I'm"):当你追加一个切片时,它会检查它是否可以容纳容量内的新长度。如果没有,它将分配一个新的数组(从而分配新的内存位置)。包含“hi There”的较短切片已达到其容量,向其追加将分配一个新数组并创建一个具有更大容量的切片。

  • 如果你有这条线initialSlice = append(initialSlice, "I'm")在您的程序中,新数组将在循环之前分配。这append(...)循环内的 s 不会导致新的分配。
  • 如果您的程序中没有该行,则append(...)循环内的 s 将导致新的分配,因此每个最终都会有不同的内存位置,这就是它们不会相互覆盖的原因。

我的来源是Go Slices:用法和内部结构 https://blog.golang.org/slices-intro#TOC_4.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

循环内的切片似乎保留了前一个/最后一个引用(取决于切片的长度) 的相关文章