slice

一、构造组成

切片实质上是一个构造体slice,他的属性由上面三局部组成:

  • array:元素存哪里
  • len:曾经存了多少
  • cap:容量是多少
type slice struct {  array unsafe.Pointer //数组的指针  len   int //切片长度  cap   int //切片容量}

举个例子

咱们都晓得,go语言的办法传参数都是值传递。

因为切片类型是一个构造体,所以当传到change办法的时候,形参arr是arrOri一份正本。然而因为属性array是一个指针,所以扭转arr的值,也会影响到arrOri

func main() {  arrOri := []string{"0", "1", "2"}  change(arrOri)  fmt.Println(arrOri[0])}func change(arr []string) {  if len(arr) > 0 {    arr[0] = "test0"  }}//output:test0

二、append产生了什么?

通过下面的例子咱们晓得,如果某个办法扭转的切片某个地位的值,也会影响到原切片。

上面咱们再看一下这个例子,为什么最初输入是0,而不是test0呢?接下来咱们持续来聊聊扩容

func main() {  arrOri := []string{"0", "1", "2","3"}  change2(arrOri)  fmt.Println(arrOri[0])}func change2(arr []string) {  arr = append(arr, "4")  if len(arr) > 0 {    arr[0] = "test0"  }}//output:0

咱们最开始申明切片arrOri的时候给了4个元素,那切片的初始容量为4,array如下图的下面的数组。

但当咱们增加一个元素的时候,原先的数组的长度曾经不够咱们减少元素。

所以须要申明一个新的更长数组,把老数组的数据复制到新的数组中去,而后追加咱们须要增加的元素,最初把切片的属性array的指针指向新的数组。

所以arrOri的array属性指向的是老数组,而arr的array属性指向的是新的数组,扭转arr的值不会影响到arrOri的值。


上面是append是的源码解析

// growslice 切片容量增长函数//  扩容概括:1.须要的cap大于2倍旧cap -> 增长到新的cap//      2.旧cap小于256 -> 双倍增长//      3.旧cap大于256 -> 1.25倍+192 的速度线性扩容,直到新切片的cap大于须要的cap//  @param et 切片数据的类型//  @param old 老的切片//  @param cap 须要增长到的容量//  @return slice 返回新的切片func growslice(et *_type, old slice, cap int) slice {  //切片的数据类型的大小为0(比方struct{})  //  则数组能够不必具体的值,只须要扭转len 和 cap的值  if et.size == 0 {    return slice{unsafe.Pointer(&zerobase), old.len, cap}  }  newcap := old.cap  doublecap := newcap + newcap   if cap > doublecap {     //如果【新的切片须要的cap】>【老切片cap】的两倍,则扩容到新的切片须要的cap    newcap = cap      } else {    const threshold = 256    if old.cap < threshold {       //如果原数组长度 < 256,容量翻倍      newcap = doublecap    } else {      //如果原数组长度 >= 256,      //容量以【1.25*newcap + 192】线性速度速度缓缓扩容,直到newcap大于须要的cap                    for 0 < newcap && newcap < cap {        newcap += (newcap + 3*threshold) / 4      }      if newcap <= 0 {      //原先容量为0,则容量为参数cap        newcap = cap      }    }  }  var overflow bool  var lenmem, newlenmem, capmem uintptr    //调配对象  lenmem = uintptr(old.len) * et.size   p = mallocgc(capmem, et, true)  //把旧数据old.array复制到新数组p中  memmove(p, old.array, lenmem)    return slice{p, old.len, newcap}}

三、截断产生了什么

func main() {  a := []int{1, 2, 3, 4, 5, 6}  b := a[2:5]  a[2] = 999  fmt.Println(b)}//output:[999 4 5]

当咱们进行切片截断的时候,新切片b的array属性指针和a的array指针指向同一个数组(如下图),只是len和cap属性值不同,所以扭转a的值会影响到b的数据,如下图

下面相干代码基于go1.18

https://github.com/golang/go/...