切片长度

关于切片的面试题:摘自https://goquiz.github.io/#subslice-grow

func Subslice() {
   s := []int{1, 2, 3,4,5,6,7,8,9}
   ss := s[3:6]
   fmt.Printf("len ss : %d\n", len(ss))
   fmt.Printf("Cap ss : %d\n", cap(ss))
   ss = append(ss, 4)
   fmt.Printf("len ss : %d\n", len(ss))
   fmt.Printf("Cap ss : %d\n", cap(ss))
   for _, v := range ss {
      v += 10
   }

   for i := range ss {
      ss[i] += 10
   }

   fmt.Println(s)
}
Click and drag to move

大家可以看一下结果是什么,结果是:

Click and drag to move

下面是解析过程:

首先ss := s[3:6],结果就是截取索引在[3, 6)上的数据,所以len(ss)是3,那ss的cap容量为啥是9呢?

一个切片的容量就是该切面在底层数组山的开始位置向右扩展至数组的结束位置,在这边就是索引位置3到索引位置8 ,一共六个元素,append()操作并没有导致容量增加,因为切片容量为6,加上元素4长度才是4,不会导致切片扩容。因为切片没有扩容,引用的还是底层s数组,所以更改ss上的元素的大小,数组s上的值也会跟着改变。

大家再看下这道题的打印结果是什么:

Click and drag to move

结果是:

Click and drag to move

下面是解析过程:

想不通的是为啥改了ss的值,但是数组s不变呢。原因是,ss:=s[1:]后,ss长度为2,cap为2。append后长度大于2所以切片扩容,扩容后的切片指向新的数组,与原数组s无关,所以对ss修改值,原数组的值不会变。

下面,介绍下底层append的代码,让大家明白这个过程。

首先append函数是go的内置函数,所以但是go并不开放内置函数的详细代码(如果有人看到源码,请在博客评论中告诉我,谢谢),所以我只在builtin.go中找到如下代码:

Click and drag to move

对于append函数的细节,注释里讲了,内置函数将新元素接到切片的后面,如果切面有足够的容量,则切片可以容纳新元素。如果切面容量不够,一个新的底层数组将会被分配。李笑来老师说过:english + compute skills = freedom。大家多看看源码还是有好处的。继续看下源码slice.go下的这个growslice 函数(主要用于处理append过程中Slice的增长),主要看下cap容量这个参数是怎么变化的?分析过程见下面的代码中文注释

Click and drag to move

容量增长的过程:

1、输入参数,切片,需要的容量

2、要是需要的容量比两倍老容量都大,那新的容量大小就是需要的容量大小

3、要是需要的容量比两倍老容量小:a、且老切片的长度小于1024个,则新容量大小为老容量的2倍 b、老切片长度大于1024,则老切片长度*(1.25)^n直达老切片长度大小超过需要的容量。

所以第二题的cap为4,原先cap为2,需要的cap为3,因为老切片长度为2小于2014所以直接老切片长度两倍就可以了。然后第二题的数组s的值没有变的原因是,Append发现老切片cap已经不够了,需要申请新切片。新切片底层数组就不是原来的数组了,所以对切片ss每个元素+10,是不影响老数组的元素的。

csdn博客:https://blog.csdn.net/u013276277

最后更新于

这有帮助吗?