Go Go 的内建函数 append 为什么会返回一个新的 slice?

willx · March 01, 2023 · Last by peterparker replied at March 07, 2023 · 512 hits

Go 源码中对于 slice 的声明是这样的:

type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

并且切片还是一个指针,所以可以认为 s := make([]string, 10) 约等于 s := &slice{}

append 方法既然接收 *slice 作为参数,为什么还会返回一个新的 slice 呢?它完全可以修改当前的 slice 呀,再创建一个 *slice 返回不是会产生更多的内存申请和释放吗?

如果直接修改当前 slice,那其他 slice 和当前 slice 用的同一个指针,数据不也被改了吗

如果按照 slice 是一个结构体来理解,那下面这段代码的输出就很难理解了。

s := make([]int, 0, 2)
s2 := s
fmt.Printf("我们地址都一样%p\n", s)
s = append(s, 10)
fmt.Printf("我们地址都一样%p\n", s)
s = append(s, 20)
fmt.Printf("我们地址都一样%p\n", s)
s3 := s
s = append(s, 30)
fmt.Printf("地址不一样了!%p\n", s)

fmt.Printf("%v, %p\n", s3, s3)
fmt.Printf("%v, %p\n", s2, s2)

可以看到当 cap 没有扩容的时候,s 作为指针输出的结果是一样的,指针相等应该说明底层指向同一个 slice 变量。

但是又发现 s2 和 s3 的地址虽然是一样,可是输出的内容不一样,说明这个指针实际上指向的是 slice 底层的数组,而不是 slice 变量,s2 和 s3 并不是指向同一个 slice 的,很明显他们的 len 是不同的

这就很诡异了。

感觉可能是 Go 有一个自动拆箱之类的操作。

type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

虽然说结构体声明是这样,但是实际代码中 slice 会被拆箱直接展示 array 字段给我们

切片是基于数组的我记得,就算自己创建一个切片,底层也是先创建一个数组,然后再让切片指向它,而数组又是不可变的,如果容量足够的情况下还好,如果容量不够,那就要扩容

willx closed this topic. 07 Mar 11:43
You need to Sign in before reply, if you don't have an account, please Sign up first.