以下两个函数执行结果一样吗?为什么?
func f2() {
ia := [...]int{1, 2, 3, 4, 5}
ia2 := ia[1:3]
for i := 6; i < 10; i++ {
ia2 = append(ia2, i)
}
fmt.Println(ia, ia2)
}
func f1() {
ia := [...]int{1, 2, 3, 4, 5}
ia2 := ia[1:3]
ia2 = append(ia2, 6,7,8,9)
fmt.Println(ia, ia2)
}
踩坑分析:
切片(slice)陷阱
考虑到性能原因,再次切片一个切片不会复制底层的数组。这是一个值得赞赏的目标,但也意味着切片的子切片只是遵循原始切片变化的视图。因此,如果您想要将它与初始的切片分开请不要忘记 copy()。对于 append 函数,忘记 copy() 会变得更加危险:如果它没有足够的容量来保存新值,底层数组将会重新分配内存和大小。这意味着 append 的结果能不能指向原始数组取决于它的初始容量。这会导致难以发现的不确定 bugs。
分别观察一下f1和f2函数的输出,f1函数里的是直接开辟了一块新的内存去存储,f2函数里的是先在原基础上上写的。
f1中ia2 := ia[1:3]后cap(ia2)的长度是4,因为ia2 = append(ia2, 6,7,8,9),会超出4,所以直接分配一块内存,在新的内存上写。
f2中ia2 := ia[1:3]后cap(ia2)的长度是4,因为逐个写的,第一次append(ia2, i)之后,没有超出4,第二次操作也没有,所以改动了原来的ia,第三次的时候,超出了4,就重新分配一个块cap(ia2)为8的内存上,并把值拷贝过去。
func f2() {
ia := [...]int{1, 2, 3, 4, 5}
ia2 := ia[1:3]
fmt.Printf("len: %d, cap: %d\n",len(ia2), cap(ia2))
fmt.Printf("%v %v\n", unsafe.Pointer(&ia[0]), unsafe.Pointer(&ia2[0]))
fmt.Printf("%v %v\n", unsafe.Pointer(&ia[1]), unsafe.Pointer(&ia2[1]))
fmt.Printf("%v\n", unsafe.Pointer(&ia[2]))
fmt.Printf("%v\n", unsafe.Pointer(&ia[3]))
fmt.Printf("%v\n", unsafe.Pointer(&ia[4]))
for i := 6; i < 10; i++ {
fmt.Println("------------")
ia2 = append(ia2, i)
fmt.Printf("len: %d, cap: %d\n",len(ia2), cap(ia2))
fmt.Printf("%v %v\n", unsafe.Pointer(&ia), unsafe.Pointer(&ia2))
fmt.Printf("%v %v\n", unsafe.Pointer(&ia[1]), unsafe.Pointer(&ia2[1]))
if len(ia2) < 3 {
fmt.Printf("%v\n", unsafe.Pointer(&ia[2]))
} else {
fmt.Printf("%v %v\n", unsafe.Pointer(&ia[2]), unsafe.Pointer(&ia2[2]))
}
if len(ia2) < 4 {
fmt.Printf("%v\n", unsafe.Pointer(&ia[3]))
} else {
fmt.Printf("%v %v\n", unsafe.Pointer(&ia[3]), unsafe.Pointer(&ia2[3]))
}
if len(ia2) < 5 {
fmt.Printf("%v\n", unsafe.Pointer(&ia[4]))
} else {
fmt.Printf("%v %v\n", unsafe.Pointer(&ia[4]), unsafe.Pointer(&ia2[4]))
}
if len(ia2) == 6 {
fmt.Printf("%v\n", unsafe.Pointer(&ia2[5]))
}
}
fmt.Println(ia, ia2)
}
func f1() {
ia := [...]int{1, 2, 3, 4, 5}
ia2 := ia[1:3]
fmt.Printf("%v %v\n", unsafe.Pointer(&ia), unsafe.Pointer(&ia2))
fmt.Printf("%v %v\n", unsafe.Pointer(&ia[0]), unsafe.Pointer(&ia2[0]))
fmt.Printf("%v %v\n", unsafe.Pointer(&ia[1]), unsafe.Pointer(&ia2[1]))
fmt.Printf("%v\n", unsafe.Pointer(&ia[2]))
fmt.Printf("%v\n", unsafe.Pointer(&ia[3]))
fmt.Printf("%v\n", unsafe.Pointer(&ia[4]))
fmt.Println("------------")
ia2 = append(ia2, 6,7,8,9)
fmt.Printf("%v %v\n", unsafe.Pointer(&ia), unsafe.Pointer(&ia2))
fmt.Printf("%v %v\n", unsafe.Pointer(&ia[0]), unsafe.Pointer(&ia2[0]))
fmt.Printf("%v %v\n", unsafe.Pointer(&ia[1]), unsafe.Pointer(&ia2[1]))
fmt.Printf("%v %v\n", unsafe.Pointer(&ia[2]), unsafe.Pointer(&ia2[2]))
fmt.Printf("%v %v\n", unsafe.Pointer(&ia[3]), unsafe.Pointer(&ia2[3]))
fmt.Printf("%v %v\n", unsafe.Pointer(&ia[4]), unsafe.Pointer(&ia2[4]))
fmt.Printf("%v\n", unsafe.Pointer(&ia2[5]))
fmt.Println(ia, ia2)
}
文章参考来源:https://studygolang.com/articles/35050