以下两个函数执行结果一样吗?为什么?
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