今天, 突然被同事卷到了, 被问到 golang 中 slice 的三个参数是干嘛的? 我突然一时间忘记了, golang 的重切片居然是可以接受第三个参数的, 枉费我已经了写了快两年的 go 了. 赶紧 google 一下, 并总结备忘.
简单介绍 slice 的数据结构#
首先, 介绍一下 golang 中切片的结构体:
1
2
3
4
5
| type slice struct {
array unsafe.Pointer
len int
cap int
}
|
有三个字段:
- array 是切片所指向的底层数组数据
- len 就是切片的长度
- cap 即容量, 很明显
源码地址
简单版重切片 a[low:high]
#
- 接受切片中的开始下标和结束下标, “左闭右开原则” 即重的切片数据会包含 low 下标的值, 但没有 high 下标的值
- 新切片的 容量(cap) 即为开始下标到原 slice 数据容量结束, 即"cap(a) - low"
- low 参数可以省略, 默认从 0 下标开始
- high 参数也可以省略, 默认就是 slice 的长度, 即 len 的 value
1
2
3
4
5
6
7
8
| a := [10]int{}
oldSlice := a[:5]
newSlice := a[2:4]
fmt.Printf("b: len %d, cap %d, c: len %d, c:cap %d", len(b), cap(b), len(c), cap(c))
输出:
b: len 5, cap 10, c: len 2, c:cap 8
|
底层分配情况如下:
1
2
3
4
5
6
7
| 底层数组 : [0 0 0 0 0 0 0 0 0 0]
旧切片容量: 10 : [0 0 0 0 0 0 0 0 0 0]
旧切片长度: 5 : [0 0 0 0 0]
新切片容量: 8 : [0 0 0 0 0 0 0 0]
新切片长度: 2 : [0 0]
|
完整版重切片 a[low:high:max]
#
完整版是为了补充简单版, 新切片会默认拥有从开始下标后的所有底层数组容量. 即新数组拥有修改全部数据底层能力 (重切片出来的不同切片, 在没有 append 操作触发重新分配底层数组的前提下, 指向的都是同一个数据, 修改数据相互可见)
- 增加了 max 参数, 表示新切片可以获取到最大的原切片的容量大小.
1
2
3
4
5
6
7
8
| a := [10]int{}
oldSlice := a[:5]
newSlice := a[2:4:6]
fmt.Printf("b: len %d, cap %d, c: len %d, c:cap %d", len(b), cap(b), len(c), cap(c))
输出:
b: len 5, cap 10, c: len 2, c:cap 4
|
底层分配情况如下
1
2
3
4
5
6
7
| 底层数组 : [0 0 0 0 0 0 0 0 0 0]
旧切片容量:10 : [0 0 0 0 0 0 0 0 0 0]
旧切片长度:5 : [0 0 0 0 0]
新切片容量:4 : [0 0 0 0]
新切片长度:2 : [0 0]
|
但是, 如果 max 要求获取的容量大于旧数据容量. 可想而知, 那一定会 panic
1
2
3
4
5
6
7
| a := [10]int{}
b := a[:4:4]
c := b[0:4:5]
输出:
panic: runtime error: slice bounds out of range [::5] with capacity 4
|
所以参数要求: 0 <= low <= high <= max <= cap(a)