slice其实是具有一个包含指向连续内存区域的指针,表示当前元素个数的整形变量和当前切片能够容纳的元素个数的整形变量的结构体
赋值操作仅仅是开辟一个新的指针指向同一片内存空间。并赋值元素个数和从指针位置开始算起到末尾的容量。
容量在我看来,就是预先留好的空间。不足就要扩容。 appen时的问题,如果一个数组array:=[4]int{10,20,30,40} slice:=array[1,3](20,30) 长度为2 容量为3 所以这时newslice:=append(slice,60) array={10,20,30,60} 因为对slice来说 容量还够,所以直接在原数组后添加,导致原来的呗覆盖。 但如果此时slice:=array[:] 容量为4 append由于容量不足会开辟一片新的内存空间来存放原来的元素并添加最新的元素。 区别就在于容量是否充足,而这里就出现有以上的一个小bug。
以上的解决方法 slice:=array[1,3,3](20,30) 通过限制新切片的容量,使得在新appen的时候会重新开辟内存存储,而不会改动到原来的数组;
slice 声明
func testSlice(){
var s1 []int
fmt.Printf("s1.p:%p\n",&s1)
fmt.Printf("s1 : v:%v,p:%p,len:%v,cap:%v\n",s1,s1,len(s1),cap(s1))
fmt.Printf("s1 == nil is : %v \n ",s1==nil)
s2 := make([]int,0)
fmt.Printf("s2 : v:%v,p:%p,len:%v,cap:%v\n",s2,s2,len(s2),cap(s2))
fmt.Printf("s1 == nil is : %v \n ",s2==nil)
s3 := make([]int,0)
fmt.Printf("s3 : v:%v,p:%p,len:%v,cap:%v\n",s3,s3,len(s3),cap(s3))
s3 = append(s3, 2)
s3 = append(s3, 2)
s3 = append(s3, 2)
fmt.Printf("s3 : v:%v,p:%p,len:%v,cap:%v\n",s3,s3,len(s3),cap(s3))
}
var 分配了内存! s1本身作为一个结构体,他是有被分配到内存并存储起来的,只是他的内部的地址,长度,容量分别为nil,0,0 ;此时的slice是一个nil slice; s2 := make([]int,0) // nake([]array,len,cap]) make([]array,len&cap])
如上,make一个空切片,长度容量都为空,底层没有为数组分配存储空间,但是指针并不为nil;
以上代码输出结果
s1.p:0xc000004440
s1 : v:[],p:0x0,len:0,cap:0
s1 == nil is : true
s2 : v:[],p:0x5791c8,len:0,cap:0
s1 == nil is : false
s3 : v:[],p:0x5791c8,len:0,cap:0
s3 : v:[2 2 2],p:0xc000010340,len:3,cap:4
slice每次扩容,虽然乍看之下是小于1024个时翻倍,大于1024时翻1.25倍,然后事实上在内存分配的时候并不是就这么直接的去计算;他是会先计算本次appen后最少需要多少容量,然后再计算出原来的两倍,进行比较,如果最少需要大于两倍当前容量,会以最少需要为主;而且这还不是最终确定,在申请内存的时候还会进行内存对齐,最终的容量是实际申请到的大小;比如原来是2个,插入3个,需要的容量是5,原来翻倍后是4,此时在进行申请,需要容量为5的内存大小,然而go中内存分配是一次按特定span的中object的大小进行分配,他需要5个,但是距离他最近的span规格每个object是可以容纳该元素6个大小,所以最后返回的会是6;