揭秘 Go 切片指针与值传递:为何函数内操作会影响外部切片?
2023-09-04 01:45:03
切片值传递与引用传递
在 Go 语言中,函数参数默认是值传递,这意味着函数内的参数副本与函数外的原始变量是分开的。但是,切片是一种引用类型,它存储的是底层数组的地址。因此,当函数的参数是切片时,实际上是将切片指向的底层数组的地址传递给了函数。这意味着,函数内的任何修改都会反映到函数外部的原始切片中。
以下是一个简单的例子来说明这一点:
package main
import "fmt"
func main() {
// 定义一个切片
slice := []int{1, 2, 3}
// 将切片传递给函数
modifySlice(slice)
// 打印切片
fmt.Println(slice)
}
func modifySlice(slice []int) {
// 对切片进行排序
sort.Ints(slice)
}
在上面的例子中,modifySlice
函数的参数是一个切片。当函数被调用时,切片指向的底层数组的地址被传递给了函数。函数内的 sort.Ints(slice)
语句对切片进行排序,这将修改切片指向的底层数组。当函数返回时,对切片的修改会反映到函数外部的原始切片中。因此,在主函数中打印切片时,将会输出排序后的切片。
向切片添加元素
当在函数内向切片添加新元素时,这些新元素不会添加到函数外部的原始切片中。这是因为原始切片的容量是固定的,无法动态扩展。因此,当在函数内向切片添加新元素时,实际上是创建了一个新的切片,指向一个更大的底层数组。这个新切片与函数外部的原始切片是分开的,因此,函数内的修改不会反映到函数外部的原始切片中。
以下是一个例子来说明这一点:
package main
import "fmt"
func main() {
// 定义一个切片
slice := []int{1, 2, 3}
// 将切片传递给函数
add元素(slice)
// 打印切片
fmt.Println(slice)
}
func add元素(slice []int) {
// 向切片添加一个元素
slice = append(slice, 4)
}
在上面的例子中,add元素
函数的参数是一个切片。当函数被调用时,切片指向的底层数组的地址被传递给了函数。函数内的 slice = append(slice, 4)
语句向切片添加了一个元素。但是,由于原始切片的容量是固定的,因此实际上是创建了一个新的切片,指向一个更大的底层数组。这个新切片与函数外部的原始切片是分开的,因此,在主函数中打印切片时,将会输出原始切片,不会包含函数内添加的新元素。
排序切片
当对函数内的切片进行排序时,函数外部的原始切片中的元素顺序也会发生改变,但元素数量保持不变。这是因为排序操作只是改变了切片中元素的顺序,而不会改变切片的容量。因此,当函数返回时,对切片的修改会反映到函数外部的原始切片中,但原始切片的元素数量保持不变。
以下是一个例子来说明这一点:
package main
import (
"fmt"
"sort"
)
func main() {
// 定义一个切片
slice := []int{3, 1, 2}
// 将切片传递给函数
sortSlice(slice)
// 打印切片
fmt.Println(slice)
}
func sortSlice(slice []int) {
// 对切片进行排序
sort.Ints(slice)
}
在上面的例子中,sortSlice
函数的参数是一个切片。当函数被调用时,切片指向的底层数组的地址被传递给了函数。函数内的 sort.Ints(slice)
语句对切片进行排序,这将改变切片指向的底层数组中元素的顺序。当函数返回时,对切片的修改会反映到函数外部的原始切片中。因此,在主函数中打印切片时,将会输出排序后的切片。
总结
通过上面的三个小测验,我们可以看到,Go 切片在函数内的操作可能会影响函数外部的原始切片,也可能不会。这取决于函数内对切片的具体操作。如果函数内对切片进行了排序,那么函数外部的原始切片中的元素顺序也会发生改变。如果函数内向切片添加了新元素,那么函数外部的原始切片不会包含这些新元素。为了避免在函数内对切片进行修改而影响函数外部的原始切片,我们可以使用切片的拷贝来进行操作。