slice 的底层实现是一个包含三个字段的结构体:指向底层数组的指针、slice 的长度和 slice 的容量。当我们对 slice 进行操作时,例如添加或删除元素,实际上是在底层数组中进行操作。由于 slice 是一个指向底层数组的指针,因此多个 slice 可以共享同一个底层数组,这也是 slice 被称为引用类型的原因,也因此可以将其赋值为 nil(实际上是将底层结构体中的指向底层数组的指针设置为 nil)。
需要注意的是,虽然 slice 是引用类型,但是它并不是一个指针类型。因此,我们可以对 slice 进行赋值和传递,而不需要使用指针。这也是 Go 语言中 slice 的一个优点,它可以方便地进行传递和复制,而不需要担心底层数组的复制和传递带来的性能问题。
除了 slice,Go 语言中还有其他的引用类型,例如 map 和 channel
这里要分具体情况,如果在函数中对 slice 进行了 append 操作导致了 slice 扩容,那么扩容之后的 slice 的底层数据与原 slice 分离,就不会相互影响,否则会影响原 slice
// 初始化一个长度和容量不相同的 slice,函数中 append 操作没有导致底层数据扩容
// 所以对 slice[0] 的修改会影响原 slice
func Test_isSliceChangeIfAppend(t *testing.T) {
slice := make([]int, 5, 10)
fmt.Printf("origin slice:%v\n", slice)
isSliceChangeIfAppend(slice)
fmt.Printf("after append out func:%v\n", slice)
}
func isSliceChangeIfAppend(origin []int) {
origin = append(origin, 1, 2, 3)
origin[0] = 1
fmt.Printf("after append in func:%v\n", origin)
}
-----output-----
origin slice:[0 0 0 0 0]
after append in func:[1 0 0 0 0 1 2 3]
after append out func:[1 0 0 0 0]
// 同上面的代码,初始化一个长度和容量相同的 slice
// 函数中的 append 操作就一定会扩容,后续修改也就不会影响原 slice
func Test_isSliceChangeIfAppend(t *testing.T) {
// 初始化一个长度和容量相同的 slice
slice := make([]int, 5)
fmt.Printf("origin slice:%v\n", slice)
isSliceChangeIfAppend(slice)
fmt.Printf("after append out func:%v\n", slice)
}
func isSliceChangeIfAppend(origin []int) {
origin = append(origin, 1, 2, 3)
origin[0] = 1
fmt.Printf("after append in func:%v\n", origin)
}
-----output-----
origin slice:[0 0 0 0 0]
after append in func:[1 0 0 0 0 1 2 3]
after append out func:[0 0 0 0 0]
![](https://article.cdnof.com/2308/51211f39-3a5f-4ef4-8dae-3fa8960e50f8.jpg)
手机扫一扫
移动阅读更方便
你可能感兴趣的文章