Go语言切片题目解析
前言
在Go语言中,Slice作为一个比数组更加灵活的数据结构被广泛应用。但也正是由于它的灵活性,导致使用时常常容易犯错。笔者昨天做到了一道很有意思的题目,在这里与大家分享。
题目
下面的函数输出什么?
1 | package main |
A : [2, 3][][2, 3][2, 3, 4]
B : [1, 2][1, 2, 3]
C : [1, 2][2, 3, 4]
D : [2, 3, 1][2, 3, 4, 1]
给大家一些思考时间,为防不小心看到答案,此处空出几行。
答案是C。这道题比较综合地考察了append()操作切片时的一些细节。
首先,让我们看一下函数内外的切片在append操作前后发生了怎样的变化。在每一步打印出底层数组起始处的地址,代码如下:
1 | package main |
运行后,结果如下。
1 | 0xc0000ac070, len_s1: 2, cap_s1: 2 |
由下面的结果我们可以得到以下几个结论:
- 直接使用字面量初始化切片,容量默认等于长度。
- 形如s2 := s1此类的初始化方式,会共用底层数组。
- 在append后超出原切片长度时,Go会新建一个新的切片,并将容量扩充为原来的两倍(实际上,在容量小于1024时每次以两倍速度扩充,大于等于1024时以1.25倍扩充),再将原数据搬迁过去。
- 在传入切片为函数参数时,虽然Go中传参为值传递,但由于切片中指向底层数组的是指针,所以对切片底层数组值的改动依然有效。事实上为引用传递。(但函数内部对len和cap的变化并不会影响到原切片)
- 如果append后未超出原切片长度,则直接从原切片上更改值。
所以,为什么答案是C也就很明显了。首先函数外对于s2的append超出了原切片的容量,所以新建了一个切片。此时s1与s2不再是同一个切片。在s1传入函数时,由于append后长度超出容量,所以新建一个切片并在新切片上做出改动,后续操作都未能影响s1本身,故输出为[1, 2]。而s2传入函数时,append后长度未超过容量,函数内s依旧与s2指向同一个底层数组,所以值的自增有效。但由于s2的len依然为3,所以输出为[2, 3, 4]。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Chao Pang的个人主页!