美文网首页go
go 切片的底层实现

go 切片的底层实现

作者: 卖毛玉的小贩 | 来源:发表于2019-08-17 11:53 被阅读0次

    go 数组切片的底层实现

    go的切片也就是所谓的可变数组,当创建的时候,会发现大小只为24,原因就是他本质是一个结构体,存放着3个字段

    type arr struct {
       Data unsafe.Pointer
       Len  int
       Cap  int
    }
    

    从上面可以看出来,分别存放指针,长度,以及大小,可这些究竟是干什么用的呢?

    • data:存放的是数组第一个元素的指针
    • len:该数组的长度
    • cap:该数组内存大小

    要想实现切片,除了这些,还需要了解的是,数组内存是一段连续的内存空间,并且go是不允许直接在内存地址上进行计算偏移后取值的,于是我们需要引用C的3个函数

    好了,废话不多说了,上代码。

    package data
    
    /*
    #include <stdlib.h>
    */
    import "C"
    import (
       "fmt"
       "unsafe"
    )
    
    type arr struct {
       Data unsafe.Pointer
       Len  int
       Cap  int
    }
    // 设置每个元素的内存地址大小
    const Tag = 8
    // 初始化
    func (a *arr) New(l, c int, data ...int) {
       if a == nil {
          return
       }
       if data == nil {
          return
       }
       if l < 0 || c < 0 || l > c || len(data) > l {
          return
       }
      //开辟内存
       a.Data = C.malloc(C.size_t(c) * Tag)
       a.Len = l
       a.Cap = c
    
       p := uintptr(a.Data)
    
       for i, v := range data {
          *(*int)(unsafe.Pointer(p + uintptr(i*Tag))) = v
       }
    }
    // 打印
    func (a *arr) Printf() {
    
       if a == nil || a.Data == nil {
          return
       }
       p := uintptr(a.Data)
       for i := 0; i < a.Len; i++ {
          fmt.Printf("%d  ", *(*int)(unsafe.Pointer(p + uintptr(i*Tag))))
       }
       fmt.Println()
    }
    // 添加
    func (a *arr) Add(data ...int) {
       if a == nil || a.Data == nil {
          return
       }
       for a.Cap < a.Len+len(data) {
          a.Dilatation()
       }
       p := uintptr(a.Data)
       p += Tag * uintptr(a.Len)
       for i, v := range data {
          *(*int)(unsafe.Pointer(p + uintptr(i*Tag))) = v
       }
       a.Len += len(data)
    }
    // 扩容内存
    func (a *arr) Dilatation() {
       a.Data = C.realloc(a.Data, C.size_t(a.Cap)*2*Tag)
       a.Cap *= 2
    }
    // 获取对应下标的int指针
    func (a *arr)referIntPrt(i int)*int  {
       err := -1
       if a.Data == nil || a == nil {
          return &err
       }
       if i < 0 || i >= a.Len {
          return &err
       }
       p := uintptr(a.Data)
       p += uintptr(i) * Tag
       return (*int)(unsafe.Pointer(p))
    }
    // 查询
    func (a *arr) ReferIndex(i int) int {
       return *a.referIntPrt(i)
    }
    // 修改
    func (a *arr)Alter(index ,value int){
       *a.referIntPrt(index) = value
    }
    // 查询对应值的下标
    func (a *arr) ReferValue(value int) int {
       if a.Data == nil || a == nil {
          return -1
       }
       p := uintptr(a.Data)
    
       for i := 0; i < a.Len; i++ {
         //重新分配内存
          v := *(*int)(unsafe.Pointer(p + uintptr(i)*Tag))
          if v == value {
             return i
          }
       }
       return -1
    }
    //指定位置之后添加  or 插入
    func (a *arr) AddIndex(index int, data ...int) {
       if a.Data == nil || a == nil {
          return
       }
       if index < 0 {
          return
       }
       if index >= a.Len-1 {
          for _, v := range data {
             a.Add(v)
          }
          return
       }
       for a.Len+len(data) > a.Cap {
          a.Dilatation()
       }
       // 获取本次添加后最后元素的指针地址
       p := uintptr(a.Data)
       p += uintptr(a.Len+len(data)-1) * Tag
       // 获取添加前数组元素最后位置的指针地址
       p2 := uintptr(a.Data)
       p2 += uintptr(a.Len-1) * Tag
       // 挪动已有在index的数据到尾巴处
       for i := index; i < a.Len+len(data); i++ {
          *(*int)(unsafe.Pointer(p - uintptr(i-index)*Tag)) = *(*int)(unsafe.Pointer(p2 - uintptr(i-index)*Tag))
       }
       //获取需要复写元素的指针地址
       p3 := uintptr(a.Data)
       p3 += uintptr(index) * Tag
       // 开始复写,安放新添加的元素
       for i, v := range data {
          *(*int)(unsafe.Pointer(p3 + uintptr(i)*Tag)) = v
       }
       a.Len += len(data)
    
    }
    // 删除数组
    func (a *arr) Delect(index int) {
       a.DelectLen(index, 1)
    }
    // 删除全部数组
    func (a *arr) DelectAll() {
       a.Len = 0
    }
    // 删除固定范围数组
    func (a *arr) DelectLen(index, len int) {
       if a == nil || a.Data == nil {
          return
       }
       if index < 0 || len < 0 {
          return
       }
       // 获取删除起始位置元素的地址
       p := uintptr(a.Data)
       p += uintptr(index) * Tag
       // 获取删除范围后的元素地址
       p1 := p + uintptr(len)*Tag
       for i := 0; i < a.Len-len-index; i++ {
          *(*int)(unsafe.Pointer(p + uintptr(i)*Tag)) = *(*int)(unsafe.Pointer(p1 + uintptr(i)*Tag))
       }
       a.Len -= len
    
    }
    // 删除指定值
    func (a *arr) DelectValue(value int) {
       for {
          i := a.ReferValue(value)
          if i == -1 {
             return
          }
          a.Delect(i)
       }
    }
    
    func NewArr() {
       arr := new(arr)
       arr.New(3, 3, 1, 2, 3)
       arr.Printf()
       arr.Add(9, 8, 7, 5, 4, 3, 88, 66, 11, 55, 001,1,2,3)
       arr.Printf()
       fmt.Println(arr.ReferIndex(5))
       fmt.Println(arr.ReferValue(7))
       arr.AddIndex(4, 12, 13, 14)
       arr.Printf()
       arr.DelectValue(1)
       arr.Printf()
       arr.DelectLen(3,4)
       arr.Printf()
       arr.Alter(1,100)
       arr.Printf()
    }
    

    相关文章

      网友评论

        本文标题:go 切片的底层实现

        本文链接:https://www.haomeiwen.com/subject/zkbhsctx.html