从一道例子开始
package main
import (
"fmt"
)
func main() {
// 初始化一个 slice
slice := []int{1, 2, 3}
fmt.Println("原始 slice:", slice, len(slice), cap(slice))
// 直接传递 slice
modifySlice(slice)
fmt.Println("直接传递 slice 后:", slice, len(slice), cap(slice)) // 直接传递 slice 后的修改会影响底层数组,但不影响 len 和 cap
// 传递 slice 的指针
modifySlicePointer(&slice)
fmt.Println("传递 slice 指针后:", slice, len(slice), cap(slice)) // 传递 slice 指针后,函数修改了 slice 的长度和容量
}
// 直接传递 slice 的副本
func modifySlice(s []int) {
s[0] = 10 // 修改底层数组的值会影响原始 slice
s = append(s, 4) // 这里的修改不会影响原始 slice 的长度和容量。后续的输出slice不会包含4
}
// 传递 slice 的指针
func modifySlicePointer(s *[]int) {
(*s)[0] = 20 // 修改底层数组的值会影响原始 slice
*s = append(*s, 5) // 通过指针修改了 slice 的长度和容量
}
代码解释
直接传递切片:
modifySlice(slice)
直接将切片slice
传递给函数。因为切片本质上是一个结构体,传递切片实际上是传递了一个副本,包括指向底层数组的指针、长度和容量。- 在
modifySlice
函数中,首先将切片的第一个元素修改为10
,这会影响到原始切片,因为切片的底层数组是共享的。 - 然后调用
append(s, 4)
,将4
添加到切片中。由于这个操作导致底层数组容量不足,Go 会分配新的底层数组,生成的新切片指向这个新的数组,因此这个修改不会影响到原始切片的长度和容量。 - 在
main
中打印出slice
的状态,显示切片的第一个元素被修改为10
,但长度和容量未变,并且不包含新添加的4
:直接传递 slice 后: [10 2 3] 3 3
。
传递切片指针:
modifySlicePointer(&slice)
将slice
的指针传递给函数。传递指针意味着函数可以直接修改原始切片的长度、容量和内容。- 在
modifySlicePointer
函数中,同样首先修改切片的第一个元素为20
,这会直接影响到原始切片。 - 然后再次调用
append
添加5
,这次因为通过指针传递,原始切片的长度和容量也被修改了。 - 返回
main
函数后,打印slice
的状态,显示切片的第一个元素变为20
,并且切片长度增加到 4,包含了新添加的5
:传递 slice 指针后: [20 2 3 5] 4 6
。
图解实例
modifySlice
s[0] = 10 // 修改底层数组的值会影响原始 slice
s = append(s, 4) // 这里的修改不会影响原始 slice 的长度和容量。后续的输出slice不会包含4
对应图示例为:
modifySlicePointer
(*s)[0] = 20 // 修改底层数组的值会影响原始 slice
代码对应图示例为:
*s = append(*s, 5) // 通过指针修改了 slice 的长度和容量
代码对应图示例为:
总结:
- 直接传递切片:会影响切片的内容(因为共享底层数组),但不会改变切片的长度和容量。
- 传递切片指针:不仅可以修改切片的内容,还可以改变切片的长度和容量。