问题由来。
package main /* #include <stdlib.h> void* makeslice(size_t memsize) { return malloc(memsize); } */ import "C" import "unsafe" func makeByteSlize(n int) []byte { p := C.makeslice(C.size_t(n)) return ((*[1 << 31]byte)(p))[0:n:n] } func freeByteSlice(p []byte) { C.free(unsafe.Pointer(&p[0])) } func main() { s := makeByteSlize(1<<31) s[len(s)-1] = 255 print(s[len(s)-1]) //在这里加一句s = append(s,1),最终会报错 freeByteSlice(s) }上方是一个利用CGO机制申请超过2GB大小的slice的程序,但是我在后面加上一句append语句后,就会报内存错误。
后面发现,是因为append导致了扩容,触发了内存搬移,而free函数传入的指针仍然指向了搬移之前的内存段,这就导致了错误。
下面是关于slice的一些需要注意的点。
对于slice的赋值语句,如果没有指定容量,那么容量会与赋值等式的右值一致。 var arr = []int{0,1,2} nrr := arr[:1]上方语句执行后,nrr的cap是3而不是1。
2.当slice不断append直到超过容量时,容量为变为原来的2倍。
3.当slice发生容量改变时,会发生内存搬移。
var arr = []int{0,1,2} nrr := arr[:2:2] println(unsafe.Pointer(&arr[1])) println(unsafe.Pointer(&nrr[1])) nrr = append(nrr,3) println(unsafe.Pointer(&arr[1])) println(unsafe.Pointer(&nrr[1]))当nrr的容量扩充后元素地址发生了改变,但是原本的那部分内存并没有被回收,因为arr仍然在引用那部分内存。如果没有这种引用关系,只是单纯对一个slice进行不断的append的话,原有的内存是会被回收的。slice的内容部分永远都是连续的内存空间。
4.用切片运算符赋值时限定容量可以限定影响
var arr = []int{0,1,2} nrr := arr[:2:2] nrr = append(nrr,4) println(arr[2]) //output : 2 var arr = []int{0,1,2} nrr := arr[:2] nrr = append(nrr,4) println(arr[2]) //output : 4从上面两个程序就可以看出设置容量的重要性了,第一段代码设置了容量为2,当发生扩容的时候,内存会进行搬移,之后arr和nrr的内容就不是同一段内存了,这样nrr的第三个元素,也就是4,不会影响到arr的内存。
第二个程序中nrr的初始容量是3,因此append之后并不需要扩容,因此arr和nrr的内容分布在同一段内存,这样就导致arr的第三个元素被4覆盖。
