Golang将一个项目追加到一个切片

为什么一直保持不变? append()是否生成一个新的切片?

 package main import ( "fmt" ) var a = make([]int, 7, 8) func Test(slice []int) { slice = append(slice, 100) fmt.Println(slice) } func main() { for i := 0; i < 7; i++ { a[i] = i } Test(a) fmt.Println(a) } 

在你的例子中, Test函数的slice参数在调用者的作用域中接收variablesa 一个副本

由于一个切片variables包含一个“切片描述符”,它只引用一个底层数组,所以在你的Test函数中,你可以连续多次修改slicevariables中的切片描述符,但是这不影响调用者及其variables。

Test函数内部,第一个append重新分配slicevariables下的backing数组,将其原始内容复制过来,并附加100 ,这就是你正在观察的内容。 从Test退出时, slicevariables超出了范围,并且slice引用的(新)底层数组也是如此。

如果你想使Testappend一样行事, 你必须从它那里返回新的切片 – 就像append一样 – 并且要求Test的调用者以和append相同的方式使用它:

 func Test(slice []int) []int { slice = append(slice, 100) fmt.Println(slice) return slice } a = Test(a) 

请仔细阅读这篇文章,因为它基本上向你展示了如何实现手工append ,在解释切片如何在内部工作。 然后阅读这个 。

典型的append用法是

 a = append(a, x) 

因为append可以在原地修改它的参数, 或者根据其input的大小和容量,用一个附加的条目返回它的参数的一个副本。 使用以前附加的切片可能会给出意想不到的结果,例如

 a := []int{1,2,3} a = append(a, 4) fmt.Println(a) append(a[:3], 5) fmt.Println(a) 

可能会打印

 [1 2 3 4] [1 2 3 5] 

注意如果cap不够用, append会生成一个新的slice。 @ kostix的回答是正确的,或者你可以通过指针传递slice参数!

试试这个,我觉得很清楚。 底层数组被改变,但我们的切片不是, print只是打印len()字符,通过另一个切片cap() ,你可以看到更改的数组:

 func main() { for i := 0; i < 7; i++ { a[i] = i } Test(a) fmt.Println(a) // prints [0..6] fmt.Println(a[:cap(a)] // prints [0..6,100] } 

为了使你的代码无需从Test返回切片,你可以传递一个像这样的指针:

 package main import ( "fmt" ) var a = make([]int, 7, 8) func Test(slice *[]int) { *slice = append(*slice, 100) fmt.Println(*slice) } func main() { for i := 0; i < 7; i++ { a[i] = i } Test(&a) fmt.Println(a) } 

Go采取更精简和懒惰的方法来做到这一点。 它不断修改相同的底层数组,直到达到一个分片的容量。

Ref: http : //criticalindirection.com/2016/02/17/slice-with-a-pinch-of-salt/

来自链接的示例输出解释了Go中切片的行为。

创build切片a。

 Slice a len=7 cap=7 [0 0 0 0 0 0 0] 

切片b是指切片a中的2,3,4个索引。 因此,容量是5(= 7-2)。

 b := a[2:5] Slice b len=3 cap=5 [0 0 0] 

修改切片b也修改一个,因为它们指向相同的基础数组。

 b[0] = 9 Slice a len=7 cap=7 [0 0 9 0 0 0 0] Slice b len=3 cap=5 [9 0 0] 

附加1切片b。 覆盖一个。

 Slice a len=7 cap=7 [0 0 9 0 0 1 0] Slice b len=4 cap=5 [9 0 0 1] 

附加2切片b。 覆盖一个。

 Slice a len=7 cap=7 [0 0 9 0 0 1 2] Slice b len=5 cap=5 [9 0 0 1 2] 

将3添加到切片b。 在这里,当容量超载时,新的副本被制作。

 Slice a len=7 cap=7 [0 0 9 0 0 1 2] Slice b len=6 cap=12 [9 0 0 1 2 3] 

validation切片a和b在上一步中的容量超载之后指向不同的基础arrays。

 b[1] = 8 Slice a len=7 cap=7 [0 0 9 0 0 1 2] Slice b len=6 cap=12 [9 8 0 1 2 3] 

这是一个不错的追加切片的实现。 我猜想它与引擎盖下的情况类似:

 package main import "fmt" func main() { slice1 := []int{0, 1, 2, 3, 4} slice2 := []int{55, 66, 77} fmt.Println(slice1) slice1 = Append(slice1, slice2...) // The '...' is essential! fmt.Println(slice1) } // Append ... func Append(slice []int, items ...int) []int { for _, item := range items { slice = Extend(slice, item) } return slice } // Extend ... func Extend(slice []int, element int) []int { n := len(slice) if n == cap(slice) { // Slice is full; must grow. // We double its size and add 1, so if the size is zero we still grow. newSlice := make([]int, len(slice), 2*len(slice)+1) copy(newSlice, slice) slice = newSlice } slice = slice[0 : n+1] slice[n] = element return slice } 

我认为原来的答案不完全正确。 append()改变了切片和底层数组,即使底层数组已经改变,但仍然由两个切片共享。

按照Go Doc的规定:

切片不存储任何数据,只是描述了底层数组的一部分。 (链接)

切片只是数组周围的包装值,这意味着它们包含有关如何切片存储一组数据的底层数组的信息。 因此,默认情况下,切片在传递给另一个方法时,实际上是通过值传递的,而不是引用/指针,即使它们仍然使用相同的基础数组。 通常情况下,数组也是通过值传递,所以我假设一个切片指向一个底层数组,而不是将其存储为一个值。 关于你的问题,当你运行时把你的slice传递给下面的函数:

 func Test(slice []int) { slice = append(slice, 100) fmt.Println(slice) } 

你实际上传递了一个slice的副本和一个指向同一个底层数组的指针。这意味着,你对这个slice所做的改变并不影响main函数中的改变。 这个片本身就存储了它所切片的数组的多less信息,并向公众公开。 因此,在扩展底层数组的同时,当您运行append(slice, 1000)时,也更改了切片的切片信息,切片信息在Test()函数中保持为私有。

但是,如果您已经按照以下方式更改了代码,则可能发挥了作用:

 func main() { for i := 0; i < 7; i++ { a[i] = i } Test(a) fmt.Println(a[:cap(a)]) } 

原因是你通过在其改变的底层数组上面说a[:cap(a)]来扩展它,被Test()函数改变。 如下所述:

你可以通过重新切片来延长切片的长度,只要它有足够的容量。 (链接)