golang——slice避坑

  • slice的结构
  • append的性能损耗
  • 母子切片共享
  • 切片导致内存泄漏
  • 函数参数需要传切片的指针吗
  • 遍历slice时修改slice

slice的结构

type slice struct {
  array unsafe.Pointer
  len int
  cap int
}

slice结构如上代码,由三个field组成,分别是指向底层数组的指针,标识有效元素长度的len和标识空间大小的cap。

append的性能损耗

golang的slice类似于c++的vector,都是有len和cap的,当len

所以,如果len

append扩容:

  • 切片相对于数组最大的特点就是可以追加元素,可以自动扩容
  • 追加的元素放到预留的内存空间里,同时len+1
  • 如果预留空间已用完,则会重新申请一块更大的内存空间,capacity大约变成之前的2倍(cap<1024)或1.25倍(cap>1024)。把原内存空间的数据拷贝过来,在新内存空间上执行append操作

母子切片共享

假设有两个切片如下:

parent := make([]int, 3, 5) //len=3, cap=5
child := parent[1:3] //len=2, cap=4	
  • 刚开始,子切片和母切片共享底层的内存空间,修改子切片会反映到母切片上,在子切片上执行append会把新元素放到母切片预留的内存空间上
  • 当子切片不断执行append,耗完了母切片预留的内存空间,子切片跟母切片就会发生内存分离,此后两个切片没有任何关系

切片导致内存泄漏

func returnSubSlice() []int {
  parent := make([]int, TOTAL)
  child := parent[begin:end]
  return child
}

如上代码,假设母切片占用内存8M,child切片是在parent基础上构造的占用内存1M,子切片和母切片共享同一块内存,当函数返回后,母切片已经不在使用,本该进行释放,但是由于和子切片公用一块内存未释放母切片,造成了7M的内存泄漏,正确做法应该是给子切片开辟空间,然后for循环将需要的数据从母切片拷贝到子切片上,然后返回子切片。

函数参数需要传切片的指针吗

前边说切片的结构中又3个field:指向底层数组的指针、表示有效元素的长度len、表示空间的cap

  • 当len和cap要变时,需要传切片指针
    *arr = append(*arr, 9)
    
  • len、point和cap要变,需要传切片指针
    *arr = (*arr)[1:2]
    
  • slice中的3个field都不变,只修改底层数组,不需要传切片指针

遍历slice时修改slice

sli := []int{1, 2, 3}
//方法一
for _, v := range sli {
	v = v + 1
}
//方法二
for i, v := range sli {
	sli[i] = v + 1
}

如上两种方法种,只有方法二能成功在遍历是修改slice中的值。因为在用 for range遍历slice时,v取到的是slice中元素的拷贝,修改这个值,并不会影响元素本身。

你可能感兴趣的:(golang,golang,slice,append)