Golang Slice

90 阅读3分钟
  1. 切片在声明和初始化的时候,可以选择设置长度(len)和容量(cap)
    • 长度(len):切片的长度也可以指slice中的元素个数
    • 容量(cap):从slice的起始位置到底层数组的长度末尾之间能容纳多少个元素。切片的最大容量。却len用于小于等于cap
    • cap使用大于len,cap可以为后续slice的扩容起到到关键作用
  2. 接下来看一下源码部分
    type slice struct {
      array unsafe.Pointer // 指向底层数组的指针
      len   int // 切片的长度
      cap   int // 切片的容量
    }
  1. 引用类型。slice为引用类型,多个slice共享同一底层数组
   var data = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
   _data := data

   _data[6] = 600
   
   fmt.Println("data:", data)
   // data: [0 1 2 3 4 5 600 7 8 9]
   fmt.Println("_data:", _data)
   // _data: [0 1 2 3 4 5 600 7 8 9]

以上可以看出他们data和_data是共享一个底层数组

4.关于slice的扩容机制 当前slice长度小于等于256时,会进行一个翻倍扩容。 如果大于256时会按照 256+(256+3*256)/4这个算法去进行一个扩容。

5.slice和array的区别 slice:为引用传递,初始化时可以进行设置长度(len)和容量(cap),且支持扩容操作。 array:为值传递,固定长度。

6.当slice进行append操作 如果当前slice的长度大于容量时候,会进行扩容的情况下,还会涉及将原slice的数据复制到新分配的内存空间,以及更细切片的指针、长度容量。

7.接下来看看makeslice函数

// et *_type 元素类型的指针
func makeslice(et *_type, len, cap int) unsafe.Pointer {
    // et.size 每个元素的大小(字节数)
    // mem 切片容量所需的字节数
    // math.MulUintptr 一个安全的乘法运算 避免溢出
    // 进行安全检查 是否合法性
    mem, overflow := math.MulUintptr(et.size, uintptr(cap))
    // overflow 判断是否已出
    // 所需内存大于 maxAlloc
    // 判断长度是否合法 以及长度是否大于容量 进行合法判断
    if overflow || mem > maxAlloc || len < 0 || len > cap {
       // NOTE: Produce a 'len out of range' error instead of a
       // 'cap out of range' error when someone does make([]T, bignumber).
       // 'cap out of range' is true too, but since the cap is only being
       // supplied implicitly, saying len is clearer.
       // See golang.org/issue/4085.
       // 判断长度是否合法
       mem, overflow := math.MulUintptr(et.size, uintptr(len))
       if overflow || mem > maxAlloc || len < 0 {
          panicmakeslicelen()
       }
       panicmakeslicecap()
    }

    return mallocgc(mem, et, true)
}
  • makeslice这个放方法中
    • math.MulUintptr(et.size, uintptr(cap))
    • 注意这里使用的uintptr(cap)容量和元素的大小(字节数)做安全的乘法运算
    • 需要计算slice中元素所占用的内存是否合理,是否超出范围会出现溢出的情况。
    • 判断长度(len)的合法性,还有一个就是之前说的len必须小于cap的情况。
    • 如果以上条件满足任意一个会进行下一步判断
    • math.MulUintptr(et.size, uintptr(len))
    • 这里使用的是len和元素的大小(字节数)做安全的乘法运算
    • 进入到if中的时候就报panic
    • 若make合法将会进行mallocgc返回一个指针地址

mallocgc后边再进行分析,涉及到垃圾回收内存分类等知识。