1.13 引用类型与指针,有什么不同?

切片是一个引用类型,将它作为参数传入函数后,你在函数里对数据作变更是会实时反映到实参切片的。

func foo(s []int)  {
    s[0] = 666
}

func main() {
    slice := []int{1,2}
    fmt.Println(slice) // [1 2]
    foo(slice)
    fmt.Println(slice) // [666 2]
}

此时切片这一引用类型,是不是有点像指针的效果?是的。

但它又和指针不一样,这一点主要体现在:在形参中所作的操作并不一定都会反映在实参上。

还是以切片为例,我在形参上对切片进行扩容,发现形参扩容后,实参并没有发生改变。

func foo(s []int)  {
    s = append(s, 666)
}

func main() {
    slice := []int{1,2}
    fmt.Println(slice) // [1 2]
    foo(slice)
    fmt.Println(slice) // [1 2]
}

这是为什么呢?

这是因为当你对一个切片 append 的时候,它会做这些事情:

  1. 新建一个新的切片 slice2,其实长度与 slice1 一样,但容量是 slice1 的两倍,此时 slice2 底层指向的匿名数组和 slice1 不是同一个。

  2. 将 slice1 底层的数组的元素,一个一个的拷贝给 slice2 底层的数组。

  3. 并把扩容的元素也拷贝到 slice2中

  4. 最后把新的 slice2 返回回来,这就是为什么指针不用返回,而 slice.append 也要返回的原因

从这个流程中,可以看到等号左边的 s (slice2)和 等号右边的 s (slice1)底层引用的数组已经不是同一个了

s = append(s, 666)

因此切片的形参做扩容,并不会影响到实参。