从一道例子开始
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 的长度和容量
代码对应图示例为:
总结:
- 直接传递切片:会影响切片的内容(因为共享底层数组),但不会改变切片的长度和容量。
- 传递切片指针:不仅可以修改切片的内容,还可以改变切片的长度和容量。