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有两个数据结构创建函数:newmakenew返回一个指向已清零内存的指针,而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).

对于数组:

  1. 当数组分配给另一个副本时会复制所有元素;
  2. 数组传递给函数是值传递。

建议在平常使用中用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


Willem97
36 声望1 粉丝

不断进步,充实自己