在 Go 语言中,slice 是一个引用类型,它底层由三个部分组成:
- 指针(pointer): 指向底层数组的起始位置。
- 长度(length): 当前 slice 中元素的数量。
- 容量(capacity): 底层数组的总容量。
我们可以用下面的结构体来表示 slice 的内部实现:
type slice struct {
array unsafe.Pointer
len int
cap int
}
slice 的底层实现机制如下:
创建 slice:
- 使用
make([]T, len, cap)
或字面量[]T{...}
创建 slice。 - 初始化 slice 结构体的三个字段: 指针指向底层数组起始位置,长度和容量设置为指定值。
- 使用
访问元素:
- 通过索引访问 slice 中的元素时,Go 运行时会计算元素在底层数组中的位置,然后返回该元素的值。
append 操作:
- 当 slice 容量不足时,Go 运行时会分配一个更大的底层数组,并将原数组的元素拷贝到新数组中。
- 然后更新 slice 结构体的三个字段:指针指向新数组,长度和容量更新为新值。
切片操作:
- 创建新 slice 时,Go 只会更新 slice 结构体的指针、长度和容量字段,而不会拷贝底层数组。
- 新 slice 和原 slice 共享底层数组,这就是 slice 的"视图"特性。
内存管理:
- 当 slice 的最后一个引用被释放时,Go 垃圾收集器会自动回收底层数组占用的内存。
总之,Go 的 slice 实现非常高效和灵活。通过动态调整底层数组的容量,slice 可以自动扩展,满足程序的需求。同时,slice 的"视图"特性也使得切片操作非常方便和高效。这些特性使 slice 成为 Go 语言中非常强大和常用的数据结构之一。