本篇文章篇幅很短,主要是通过一个例子来阐述go语言中切片底层原理和使用append时值得思考的地方,放心食用!
1、抛砖引玉
下面先通过一段代码来引出我们所要阐述问题,思考输出的切片a、b、c值是什么?
package demo
import "fmt"
func main() {
var a = make([]int, 0, 2)
b := append(a, 1)
c := append(a, 2, 3)
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
}
可能会有不少同学在第一时间觉得答案是这样:
[]
[1]
[1,2,3]
明确一点:b、c的结果是错误的(埋个伏笔,请往下看)
2、心路历程
下面我们模拟,对于这个问题,我们得到上述答案的一个心路历程:
(1)关于切片a的值,应该是最没争议的一个点,因为append(a, 1)做的事情是往切片a的地址空间填充内容1,更新len并返回给接收者。代码中没有任何切片a接收append的操作,故而切片a的值为[]
(2)对于切片b的值,将append之后得到新的切片赋值给b,得到值为[1]
(3)最后就是切片c,在b这一行的代码的基础上,继续往切片a填充数据2,3,最后就是触发扩容,得到新的地址x,len和cap,并且值为[1,2,3]赋值给c,所以c的值为[1,2,3]
3、拨开云雾
正确答案
[]
[2]
[2 3]
下面我们通过打印切片的地址、长度、容量参数来辅助我们理解得到正确答案的缘由
0xc0000d8020, 0, 2
0xc0000d8020, 1, 2
0xc0000d8020, 2, 2
接下来开始逐个分析
首先,分析切片a,对于切片a、b、c都是指向同一个地址,但是a确实空切片,原因就在于这个时候其长度为0,对于go来说,此时切片a为空;
其次,分析切片b,切片b在appned(a,1)的时候,如果在没有c那一步的操作,他的长度是1,得到的答案确实也是[1],最终的答案为[2]
,原因是append(a, 2, 3)是对a的起始地址进行元素的添加,将a原有的值1覆盖掉,所有我们可以理解为此时a的内存地址为:0xc0000d8020,第1个元素为2,第2个元素为3,因为b的切片长度是1,所以得到答案[2]
最后,有了第二步的分析,可以很快的理解为什么切片c是[2,3]
4、课后作业
package demo
import "fmt"
func main() {
var a = make([]int, 0, 2)
b := append(a, 1)
c := append(a, 2, 3)
c := append(a, 4, 5,6)
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
fmt.Println(d)
}
答案是什么?欢迎在评论区一起讨论!