意外切片附加行为

Jar*_*ema 4 go

我今天去代码中遇到怪异的行为:当我追加elementsslice中环,然后尝试创建新的slices基于循环的结果,最后append覆盖slices以前appends.

在这个特殊的例子这意味着sliceFromLoop j,gh切片的最后一个元素都没有100,101102分别,但是......始终102!

第二个例子 - sliceFromLiteral表现如预期.

package main

import "fmt"

func create(iterations int) []int {
    a := make([]int, 0)
    for i := 0; i < iterations; i++ {
        a = append(a, i)
    }
    return a
}

func main() {
    sliceFromLoop()
    sliceFromLiteral()

}

func sliceFromLoop() {
    fmt.Printf("** NOT working as expected: **\n\n")
    i := create(11)
    fmt.Println("initial slice: ", i)
    j := append(i, 100)
    g := append(i, 101)
    h := append(i, 102)
    fmt.Printf("i: %v\nj: %v\ng: %v\nh:%v\n", i, j, g, h)
}

func sliceFromLiteral() {
    fmt.Printf("\n\n** working as expected: **\n")
    i := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    fmt.Println("initial slice: ", i)
    j := append(i, 100)
    g := append(i, 101)
    h := append(i, 102)
    fmt.Printf("i: %v\nj: %v\ng: %v\nh:%v\n", i, j, g, h)
}
Run Code Online (Sandbox Code Playgroud)

链接到play.golang:https: //play.golang.org/p/INADVS3Ats

经过一些阅读,挖掘和试验,我发现这个问题起源于slices引用相同的底层array 值,并且可以通过slice在附加任何内容之前复制到新的问题来解决,但它看起来很......犹豫不决.

什么是基于旧切片创建许多新切片并且不担心改变旧切片值的自动方式?

Ada*_*ith 12

不要分配append给自己以外的任何东西.

正如你在问题中提到的那样,混淆是由于append两者都改变​​了底层数组并返回一个新切片(因为长度可能会改变).你可以想象它会复制那个支持数组,但它没有,它只是分配一个slice指向它的新对象.由于i从不更改,所有这些追加都会将值更改backingArray[12]为不同的数字.

将此与append一个数组进行对比,该数组每次都会分配一个新的文字数组.

所以,是的,你需要复制切片才能使用它.

func makeFromSlice(sl []int) []int {
    result := make([]int, len(sl))
    copy(result, sl)
    return result
}

func main() {
    i := make([]int, 0)
    for ii:=0; ii<11; ii++ {
        i = append(i, ii)
    }
    j := append(makeFromSlice(i), 100)  // works fine
}
Run Code Online (Sandbox Code Playgroud)

解释了切片文字行为,因为如果追加将超过支持数组,则会分配新cap数组.这与切片文字无关,而且与超出限额的内部结构有关.

a := []int{1,2,3,4,5,6,7}
fmt.Printf("len(a) %d, cap(a) %d\n", a, len(a), cap(a))
// len(a) 7, cap(a) 7

b := make([]int, 0)
for i:=1; i<8, i++ {
    b = append(b, i)
}  // b := []int{1,2,3,4,5,6,7}
// len(b) 7, cap(b) 8

b = append(b, 1)  // any number, just so it hits cap

i := append(b, 100)
j := append(b, 101)
k := append(b, 102)  // these work as expected now
Run Code Online (Sandbox Code Playgroud)


Jim*_*imB 5

如果您需要切片的副本,除了复制切片之外没有其他方法可以做到。您几乎不应该将 的结果分配append给除 的第一个参数之外的变量append。它导致很难发现错误,并且根据切片是否具有所需的容量而表现不同。

这不是常用的模式,但与所有这种性质的事情一样,如果您需要多次重复几行代码,那么您可以使用一个小的辅助函数:

func copyAndAppend(i []int, vals ...int) []int {
    j := make([]int, len(i), len(i)+len(vals))
    copy(j, i)
    return append(j, vals...)
}
Run Code Online (Sandbox Code Playgroud)

https://play.golang.org/p/J99_xEbaWo