Go 语言的数组长度在编译期确定,无法在运行时动态扩展。为突破这一限制并提供灵活的数据操作,Go 引入了切片(Slice)。
切片在底层并非真正意义上的动态数组,而是对一段连续内存(底层数组)的描述符(Descriptor)或视图(View)。这种设计使得切片在作为函数参数传递时只需拷贝少量元数据,开销极低,同时通过运行时的辅助函数实现了动态扩容的能力。
观察 src/runtime/slice.go 的源码实现,切片的结构体由三个字段构成。在 64 位架构下,一个切片实例固定占用 24 字节:
// 适用于 Go 1.21+
type slice struct {
array unsafe.Pointer // 指向底层数组连续内存的指针
len int // 当前切片中实际元素的数量
cap int // 底层数组从切片起始位置到末尾的总容量
}array:指向底层连续内存的首地址。由于多个切片可以共享同一块底层数组,对某一“视图”的修改可能会影响其他共享该数组的切片。这带来了零拷贝的性能优势,也引入了潜在的数据竞争与覆盖风险(Trade-off)。len:当前切片的逻辑长度。任何超出len范围的索引访问都会在运行时触发panic,即使该索引仍在cap范围内。cap:当前切片的物理容量。当执行append操作且len达到cap上限时,Go 运行时会分配一块更大的新内存,将旧数据复制过去,并更新当前切片的array、len和cap字段。
