Go Slice的一点总结

在刷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中sliceappend的性质都有关。 在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
*/