slice append
首先我们看一段代码:
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)
}
运行结果如下:
initial slice: [0 1 2 3 4 5 6 7 8 9 10]
i: [0 1 2 3 4 5 6 7 8 9 10]
j: [0 1 2 3 4 5 6 7 8 9 10 102]
g: [0 1 2 3 4 5 6 7 8 9 10 102]
h:[0 1 2 3 4 5 6 7 8 9 10 102]
** working as expected: **
initial slice: [0 1 2 3 4 5 6 7 8 9 10]
i: [0 1 2 3 4 5 6 7 8 9 10]
j: [0 1 2 3 4 5 6 7 8 9 10 100]
g: [0 1 2 3 4 5 6 7 8 9 10 101]
h:[0 1 2 3 4 5 6 7 8 9 10 102]
作为初学者我们都大致知道go的数组和切片都是类似的概念,append(a, i)会扩充切片a的容量,并且把i追加到切片的最后,但是为什么上面的代码jgh相同的操作得到结果不一样呢? 我们把代码加上调试信息:
package main
import "fmt"
func create(iterations int) []int {
a := make([]int, 0)
for i := 0; i < iterations; i++ {
a = append(a, i)
//fmt.Println("cap(a) = ", cap(a), "ptr(i) = ", &a[0])
}
return a
}
func main() {
sliceFromLoop()
sliceFromLiteral()
}
func sliceFromLoop() {
fmt.Printf("** NOT working as expected: **\n\n")
a := create(11)
fmt.Println("initial slice: ", a)
j := append(a, 100)
g := append(a, 101)
h := append(a, 102)
fmt.Printf("a: %v\nj: %v\ng: %v\nh:%v\n", a, j, g, h)
fmt.Println("cap(a) = ", cap(a), "ptr(i) = ", &a[0])
fmt.Println("cap(j) = ", cap(j), "ptr(j) = ", &j[0])
fmt.Println("cap(g) = ", cap(g), "ptr(g) = ", &g[0])
fmt.Println("cap(h) = ", cap(h), "ptr(h) = ", &h[0])
}
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)
fmt.Println("cap(i) = ", cap(i), "ptr(i) = ", &i[0])
fmt.Println("cap(j) = ", cap(j), "ptr(j) = ", &j[0])
fmt.Println("cap(g) = ", cap(g), "ptr(g) = ", &g[0])
fmt.Println("cap(h) = ", cap(h), "ptr(h) = ", &h[0])
}
运行结果如下:
** NOT working as expected: **
cap(a) = 1 ptr(i) = 0xc42001c060
cap(a) = 2 ptr(i) = 0xc42001c070
cap(a) = 4 ptr(i) = 0xc420016120
cap(a) = 4 ptr(i) = 0xc420016120
cap(a) = 8 ptr(i) = 0xc420018100
cap(a) = 8 ptr(i) = 0xc420018100
cap(a) = 8 ptr(i) = 0xc420018100
cap(a) = 8 ptr(i) = 0xc420018100
cap(a) = 16 ptr(i) = 0xc42008c000
cap(a) = 16 ptr(i) = 0xc42008c000
cap(a) = 16 ptr(i) = 0xc42008c000
initial slice: [0 1 2 3 4 5 6 7 8 9 10]
a: [0 1 2 3 4 5 6 7 8 9 10]
j: [0 1 2 3 4 5 6 7 8 9 10 102]
g: [0 1 2 3 4 5 6 7 8 9 10 102]
h:[0 1 2 3 4 5 6 7 8 9 10 102]
cap(a) = 16 ptr(i) = 0xc42008c000
cap(j) = 16 ptr(j) = 0xc42008c000
cap(g) = 16 ptr(g) = 0xc42008c000
cap(h) = 16 ptr(h) = 0xc42008c000
** working as expected: **
initial slice: [0 1 2 3 4 5 6 7 8 9 10]
i: [0 1 2 3 4 5 6 7 8 9 10]
j: [0 1 2 3 4 5 6 7 8 9 10 100]
g: [0 1 2 3 4 5 6 7 8 9 10 101]
h:[0 1 2 3 4 5 6 7 8 9 10 102]
cap(i) = 11 ptr(i) = 0xc420078060
cap(j) = 22 ptr(j) = 0xc42008e000
cap(g) = 22 ptr(g) = 0xc42008e0b0
cap(h) = 22 ptr(h) = 0xc42008e160
- 首先我要注意到2种初始化切片的方式导致a的容量是16, i的容量是11
- 切片内部是由:指针(指向首地址),长度,容量3个元素组成
- sliceFromLoop 中 a的长度是11,容量是16,所以向a中append的时候不会扩容, jgh的操作后得到切片还是只向a的内存块,但是a的长度没变,所以jgh都是覆盖a的第12个元素
- sliceFromLiteral 由于 i的长度是11,容量是11,所以在jhg的appen操作要扩容是都会产生新的内存块,相当于jgh都是指向不同的内存块
func main() {
var s []int
for i := 1; i <= 3; i++ {
s = append(s, i)
}
j := append(s, 100)
m := append(s, 101)
fmt.Println(j)
fmt.Println(m)
}
[1 2 3 101]
[1 2 3 101]
这里s的cap=4,len=3, 当我们2次操作append的时候发现s都需要扩容,所以都还是执向s,所以j的值就被m覆盖了。
参考资料:
Why are slices sometimes altered when passed by value in Go?