Slice
一个slice
是一个数组某个部分的引用,在内存里,它是一个包含3个域的结构体:指向slice中第一个元素的指针,slice
的长度,以及slice
的容量。
数组的slice
并不会是实际复制一份数据,只是创建一个新的数据结构,包含另外的一个指针,一个长度和一个容量数据。
由于slice
是不同于指针的多字长结构,分割操作并不需要分配内存。
底层的实现(usr/loca/src/runtime/slice.go
)
// Go 1.12.9
type slice struct {
array unsafe.Pointer // 指向数组的指针
len int // 长度
cap int // 容量
}
ptr指针指向的底层是个数组,本质上就是使用Slice这个“引用”对数组进行操作。
slice的扩容
在对slice
进行append
等操作时,可能会造成slice的自动扩容。其扩容的大小增长规则是:
- 如果新的大小(cap)是当前大小(cap)的2倍以上,则大小增长为新大小
- 否则循环以下操作:如果当前大小(cap)小于1024,按每次2倍增长,否则每次按当前大小1/4增长。直到增长的大小超过或等于新(cap)大小。
Go有两个数据结构创建函数:new
和make
。new
返回一个指向已清零内存的指针,而make
返回一个复杂的结构。
slice与unsafe.Pointer相互转换
var o []byte
sliceHeader := (*reflect.SliceHeader)((unsafe.Pointer(&o)))
sliceHeader.Cap = length
sliceHeader.Len = length
sliceHeader.Data = uintptr(ptr)
Slice
VS Array
在Go中,slice
是值(value),非指针,非引用。
Unlike in C/C++ (where arrays act like pointers) and Java (where arrays are object references), arrays in Go are values. This has a couple of important implications: (1) assigning one array to another copies all of the elements, and (2) if you pass an array to a function, it will receive a copy of the array (not a pointer or reference to it).
对于数组:
- 当数组分配给另一个副本时会复制所有元素;
- 数组传递给函数是值传递。
建议在平常使用中用slice
而非array
,因为当使用非常多的数组时,其大量的副本,导致内存使用率很低。在Go语言的标准库中,API都是使用的slice
。
Overall, slices are cleaner, more flexible, and less bug-prone than arrays, so you should prefer using them over arrays whenever possible.
参考链接
https://juejin.im/post/6861890380888014862#heading-1
https://www.godesignpatterns.com/2014/05/arrays-vs-slices.html
有疑问加站长微信联系(非本文作者)