go语言之切片

Slice的结构如下

type slice struct {

         array unsafe.Pointer

         len   int

         cap   int

}

结构非常简单,只有三个部分:

array: 指向数组的指针。

len:当前长度。

cap:容量

 

 

Slice的初始化

var ints []int

变量ints实际上就由以下三个部分组成。
slice的元素要存在一段连续的内存中,实际上就是一个数组,但是目前只分配了这个切片结构,还没有分配底层数组。所以 data = nil | len = 0 | cap = 0 。

 

 

通过make来定义

var ints []int = make([]int, 2, 5)

这时不仅会分配这三部分结构,还会开辟一段内存作为切片的底层数组,这里make会为ints开辟一段容纳5个整型元素的内存,还会把它们初始化为整型的默认值0 。

但是目前这个slice变量只存储了两个元素,所以data指向这个底层数组的首地址 | len = 2 | cap = 5 。

这个时候我们添加一个元素,然后再做一个赋值。

ints = append(ints, 1)

ints[0] = 1

 

append的元素会被自动添加到第3个位置。

 

 

已经存储的元素是可以安全读写的,但是超出这个范围就属于越界访问。会发生panic

 

Slice的底层数组

int型slice的底层就是int型数组,string型slice的底层就是string型数组。

但是slice中的数组指针,并不是必须指向底层数组的开头。

arr := [10]{0,1,2,3,4,5,6,7,8,9} //数组容量声明了就不可改变

我们可以把不同的slice关联到同一个数组。

var s1 []int = arr[1:4]  //左闭右开

var s2 []int = arr[7:]

 

 

slice访问和修改的其实都是底层数组的元素。

如果要给s1添加两个元素,直接使用append即可,这个底层数组依然可以使用。

但是如果要给s2添加元素,这个底层数组就不能再使用了,因为数组的大小是固定的。

因此,得开辟一个新的数组。原来的元素得拷贝过来,还得添加新的元素。元素个数改为4,容量扩到了6。

 

 

Slice的扩容规则

预估规则:

1 如果扩容前容量翻倍,还是小于所需的最小容量,那么预估容量就等于所需的最小容量。

2 否则就要再细分:

  (a) 如果扩容前元素个数小于1024,那就直接翻倍。

  (b) 如果扩容前元素大于等于1024,那就先扩容至原来的 1/4 

 

 

再来看go专家编程里面的这道题

func SliceRise(s []int)  {

       s = append(s, 0)

       for i := range s {

              s[i]++

       }

}

 

func main()  {

       s1 := []int{1, 2}

       s2 := s1

       s2 = append(s2, 3)

       SliceRise(s1)

       SliceRise(s2)

       fmt.Println(s1, s2)

}

输出结果是[1,2][2,3,4]

 

1 首先s1在初始化的时候,分配了一个底层数组,len=2,cap=2 ;

将s1赋值给s2,两者就指向了同一个底层数组;

2 s2发生扩容,因为cap不够了,这个时候s2指向一个新的底层数组,并且len=3,cap=4 ;

然后调用两次SliceRise函数;

3 s1作为参数进入函数时,发生了扩容,因为cap不够了,所以新分配了一个底层数组,这个时候,main函数中的s1与SliceRise中的s1已经分道扬镳了。所以main函数中的s1不会有任何改变;

4 s2作为参数进入函数时,同样发生了扩容,但是cap还够,所以不会分配新的底层数组,接下来的所有改变都会影响到main函数中的s2;

但是为什么s2输出[2,3,4] 而不是[2,3,4,1]呢。原因在于SliceRise中s和s2虽然指向了同一个底层数组,但其实不是一个结构体。通过打印s和s2的地址就可以看到。s的len=4,cap=4, 而s2的len=3,cap=4,由于len不一样,因此s2只能取前面3个的数据。

所以最终在main函数中,s1输出[1,2],而s2输出[2,3,4]。

posted @ 2022-03-15 16:48  red_leaf_412  阅读(66)  评论(0编辑  收藏  举报