在刷leetcode的78题的时候,遇到了对slice的append的bug。
func subsets(nums []int) [][]int {
ans := [][]int{[]int{}}
for _, n := range nums {
newSets := make([][]int, 0)
for _, set := range ans {
tmp := make([]int, len(set)+1)
tmp = append(set,n)
fmt.Printf("%v\n",ans)
fmt.Printf("%d, %v, 变量的地址: %p, %v \n", n,set,&set, tmp)
newSets = append(newSets, tmp)
}
ans = append(ans, newSets...)
}
return ans
}
我们发现[9,0,3]
和[9,0,3,5]
共享一个底层数组, 当我们向[9,0,3]
append
一个7的时候,我们会覆盖[9,0,3,5]
中的5,变为[9,0,3,7]
。为了解决这个问题我们需要使用copy
,为每一个append后的切片创建一个新的数组。
copy(tmp, append(set, n))
关于Slice
那么为什么会造成这个上面的这个bug呢? 这和go中slice
和append
的性质都有关。 在Go中数组是一个值,而不是像在C中的那样是一个指向第一个元素的指针。而slice
在一定程度上更像C中的数组,slice
是对一个数组片段的描述,它由指向数组的指针,长度(length)和容量(capacity)构成。同时切片化的操作,就是创建一个指向原数组的新的切片值。
关于Append
func append(slice []Type, elems ...Type) []Type
append
, 当空间(capacity)足够时候,会直接加在slice的尾部,这时候如果slice指向的数组是一个空间足够的数组的,会直接在slice的末尾,覆盖原数组中的数(e.g. a [1,2,3,4];slice 取 [0,1] ,增加一个1,则原数组会变为[1,2,1,3]) 或者增加一个数。如果空间不够的情况下,我们会重新分配一个数组。
a := []int{1, 2, 3, 4, 5, 6, 7, 8}
b := []int{1, 2, 3, 4, 5, 6, 7, 8}
slice := a[0:2]
d := append(slice, 1)
fmt.Println(a)
fmt.Println(b)
fmt.Println(d)
/* output
[1 2 1 4 5 6 7 8]
[1 2 3 4 5 6 7 8]
[1 2 1]
*/
关于For Range
关于for range
,则是意外的收获, 我们发现在For-range
会使用同一块内存去接收循环中的值。
h := [][]int{[]int{}}
f := [][]int{[]int{1, 2, 3, 4}, []int{4, 5, 6, 7}}
for i, z := range f {
tmp := append(z, i)
fmt.Printf("变量的地址: %p\n", &z)
h = append(h, tmp)
fmt.Println(tmp)
}
/*
output:
变量的地址: 0xc00000a0e0
变量的地址: 0xc00000a0e0
*/