• golang——slice避坑


    slice的结构

    type slice struct {
      array unsafe.Pointer
      len int
      cap int
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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	
    
    • 1
    • 2
    • 刚开始,子切片和母切片共享底层的内存空间,修改子切片会反映到母切片上,在子切片上执行append会把新元素放到母切片预留的内存空间上
    • 当子切片不断执行append,耗完了母切片预留的内存空间,子切片跟母切片就会发生内存分离,此后两个切片没有任何关系

    切片导致内存泄漏

    func returnSubSlice() []int {
      parent := make([]int, TOTAL)
      child := parent[begin:end]
      return child
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

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

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

    • 当len和cap要变时,需要传切片指针
      *arr = append(*arr, 9)
      
      • 1
    • len、point和cap要变,需要传切片指针
      *arr = (*arr)[1:2]
      
      • 1
    • 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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

  • 相关阅读:
    人力资源从业者如何提升自己的能力?很实用
    “CardReordering“app Tech Support(URL)
    [Python] 面向对象(一)
    Python面向对象编程
    【第三篇】商城系统-基础业务-实现类别管理
    vscode插件
    大数据架构师——音乐数据中心平台离线数仓综合项目(一)
    开发视频会议系统:使用GPU解码渲染视频
    MyBatis--逆向工程
    Div3 cf1741
  • 原文地址:https://blog.csdn.net/qq_43747991/article/details/126843622