话不多说,首先上题。请观察以下几行代码,并说出打印结果:
func main() {
s := []int{1, 2, 3, 4}
t := s
s = append(s[1:3], 5)
fmt.Println(t)
}
很简单的几行代码,但是却要求对go语言的slice切片类型进行深入理解。一般人首先第一反应给出的答案是[2,3,5],来看看实际上的答案:
[1 2 3 5]
接下来对这几行代码进行详细讲解:
首先,切片是一个引用类型,其本质就是对底层数组的封装,它包含了三个信息(相当于一个结构体):底层数组的指针、切片的长度(len)和切片的容量(cap)。
在上述代码中,我们初始化了一个切片,其结构如下,array指向一个底层数组;
然后,将切片s通过值传递给变量t,(注意,Go里面只有值传递),然后于是变量t也有了s的这些属性值,即 len(t)=4,cap(t)=4,array指针指向与s同一个底层数组,已经赋值完毕后,不管后续s再如何操作,t的这三个成员变量值不会再发生变化,所以唯一会受到影响的是其底层数组的指针指向的数组值。
再接下来是这行代码:
s = append(s[1:3], 5)
这时发生了三件事:取底层数组的切片[1:3], 然后将5放在该切片的后面,最后将新的切片属性赋值给s。(这里要注意,不管如何操作,操作的都是同一个底层数组,底层数组的地址空间一直就摆在那里),如下图所示。
由于元素3后面已经开辟过空间地址了,所有5添加进来的时候,直接会将这块地址指向的字面值重新赋值为5,相当于将4给覆盖了。于是底层数组的值此时就变成了[1,2,3,5],
所以不管s再如何操作,t的成员变量值不再发生变化,即其底层数组的指针指向不变,只是数组值变成了[1,2,3,5]而已。