interviewer:说一说slice
interviewee: 主要包括以下几点
- slice and array
- slice的底层数据结构
- length和capacity
切片的capacity的计算规则 - 扩容
- Reslicing
- SliceHeader: slice的运行时表示
- 可以指定capacity的reslicing
1. slice and array
slice的底层存储是array,而slice只是描述底层数组的某个片段的范围,并不会实际存储数据。
2. slice的底层数据结构
A slice is a descriptor of an array segment. It consists of a pointer to the array, the length of the segment, and its capacity (the maximum length of the segment).
3. length和capacity
某个切片的capacity的计算规则
某个切片的capacity = 底层数组的cap - 该切片的prt指针在底层数组中的位置
参考附录[Re-slicing slices in Golang]
4. 扩容
- 小于1024个元素时,2倍增长; >=1024个元素时,1.25倍增长。
这个结论是不完全正确的,没有考虑到字节对齐,具体请参考附录[深度解密Go语言之Slice]
5. Reslicing
5.1 Reslicing的几个简单例子
5.2 Reslicnig过程中,length和capacity的变化 [手绘笔记]
6. SliceHeader: slice的运行时表示
https://play.golang.org/p/aKSZlVGGTpL
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
a := []byte{1, 2, 3}
// cut slice
printSlice("a", a)
printSlice("a[0:]", a[0:])
printSlice("a[1:]", a[1:])
// extend slice from a[:1] to a[1:2]
printSlice("a[:0]", a[:0])
printSlice("a[:0][:2]", a[:0][:2]) // 最开始我不理解,为什么a[:0]有0个元素,却可从中取出2个元素,后来知道了extend slice操作后才理解.
printSlice("a[:2]", a[:2])
}
func printSlice(s string, arr []byte) {
sliceHeader := (*reflect.SliceHeader)((unsafe.Pointer(&arr)))
fmt.Printf("%s %+v %v\n", s, sliceHeader, arr)
}
// output 4251681-4251680 = 1byte,说明底层数组的prt指针指向了下一个byte.
//a &{Data:4251680 Len:3 Cap:3} [1 2 3]
//a[0:] &{Data:4251680 Len:3 Cap:3} [1 2 3]
//a[1:] &{Data:4251681 Len:2 Cap:2} [2 3]
//a[:0] &{Data:4251680 Len:0 Cap:3} []
//a[:0][:2] &{Data:4251680 Len:2 Cap:3} [1 2]
//a[:2] &{Data:4251680 Len:2 Cap:3} [1 2] // 可以看出a[:0][:2]和a[:2]的结果是一样的.
7.可以指定capacity的reslicing
slice[low : high : max],具体参考[Re-slicing slices in Golang回答2] 和 [Slice expressions,Full slice expressions]
参考资料
Re-slicing slices in Golang
[Re-slicing slices in Golang回答2] (https://stackoverflow.com/a/18911267/7689674)
Slice expressions,Full slice expressions
Go Slices: usage and internals
深度解密Go语言之Slice 这是滴滴的饶大佬写的,从非常底层的角度分析的,建议预留整块时间来看。