切片

1.切片数据结构

Go 语言中切片数据结构在源码包 src 的 runtime/slice.go

type slice struct {
    array unsafe.Pointer  // 数据部分
    len   int              // 长度
    cap   int             // 容量
}

2.切片扩容

当 Go 中切片 append 当容量超过了现有容量,就需要进行扩容

  1. 确定扩容的大小

func growslice(et *_type, old slice, cap int) slice {
    // 得到旧容量大小
    newcap := old.cap
    // 扩容容量 = 旧容量 * 2
    doublecap := newcap + newcap
    // cap是新申请容量
    // 如果申请容量(cap)溢出(大于doublecap),就直接申请
    if cap > doublecap {
        newcap = cap
    // 不足,就再判断旧切片长度大于等于1024,则最终容量(newcap)从旧容量(old.cap)开始循环增加原来的1/4,即(newcap=old.cap,for {newcap += newcap/4})直到最终容量(newcap)大于等于新申请的容量(cap),即(newcap >= cap)
    } else {
        if old.len < 1024 {
            newcap = doublecap
        } else {
            // Check 0 < newcap to detect overflow
            // and prevent an infinite loop.
            for 0 < newcap && newcap < cap {
                newcap += newcap / 4
            }
            // Set newcap to the requested cap when
            // the newcap calculation overflowed.
            if newcap <= 0 {
                newcap = cap
            }
        }
    }
}
  1. 根据扩容大小和切片类型,确定不同的内存分配大小,同时保证内存的对齐。因此,申请的内存可能会大于实际的 et.size * newcap

  1. 最后核心是申请内存。要注意的是,新的切片不一定意味着新的地址。

切片复制

  • 浅拷贝

    1. 先将 data 的成员数据拷贝到寄存器,然后从寄存器拷贝到 shallowCopy 的对象中。

    2. 注意到只是拷贝了指针而已, 所以是浅拷贝

  • 深拷贝

    1. 检查切片长度与元素大小

    2. 静态分析和内存扫描

    3. 遍历内存,去复制每个元素

最后更新于