处理切片参数的必要操作#
1 简介
go语言中 所有对象都是 pass by value, 那么go语言变量为何还有 值传递和 引用传递的区别?
当有人说 “go的所有变量传递都是值传递”,这句话是真的其中也包含了一些容易混淆的细节。
2. Go 中“所有传参都是值传递”
无论传递什么类型的变量(包括指针、slice、map、chan、interface 等),它们都是 值传递。
也就是说,当你调用一个函数并传递一个变量进去时,函数接收到的是该变量的副本(拷贝),而不是变量本身。
3. 那“引用传递”的说法是怎么来的?
所谓“引用传递”,其实是指你传递的是一个 包含指针的值。虽然值本身是被拷贝的,但它 指向了原始的数据,所以你可以通过这个拷贝来修改原始数据的内容。
- 举个例子说明
值传递(传普通类型)
func changeValue(x int) {
x = 100
}
func main() {
a := 10
changeValue(a)
fmt.Println(a) // 输出仍然是 10
}
解释:a 被复制了一份传给 x,函数中修改的是副本,不影响原始变量。
-
引用“语义”的值传递(传指针)
func changePointer(x *int) { *x = 100 } func main() { a := 10 changePointer(&a) fmt.Println(a) // 输出 100 }
解释:你传的是一个指针(地址),虽然这个指针也是复制的,但它指向原始变量 a,所以可以修改它的值。
4 传 slice的解决办法(底层是引用类型)
切片 当它传递时,它与指向底层数组的指针一起传递,切片值仅包含指向实际存储元素的数组的指针。
func changeSlice(s []int) {
s[0] = 100
}
func main() {
arr := []int{1, 2, 3}
changeSlice(arr)
fmt.Println(arr) // 输出 [100 2 3]
}
虽然 arr 是通过值传递传进去的,但是 slice 本质上是一个结构体,包含一个指向底层数组的指针。这个结构体被复制了,但它仍指向原始数组。
slice 值不包括其元素(与数组不同)。slice值是一个 header ,描述后备数组的连续部分,当您将 slice 传递给函数时,将从此 Headers 中制作一个副本,包括指针,它将指向相同的后备数组。
修改 slice 的元素意味着修改 backing 数组的元素,因此共享同一 backing 数组的所有 slice 都将 “观察” 更改。
处理传递给函数的切片的最佳方法(如果切片的元素作到函数中,并且我们不希望这反映在 elements 内存块中),
则使用以下方式复制它们:copy(new_slice, *old_slice)
func changeSlice(old_slice []int) []int {
var new_slice = make([]int, len(*old_slice))
copy(new_slice, *old_slice)
for index, _ := range s {
s[index] = 1011
}
return s
}
切片是指向底层数组的指针。指针被复制,但它仍然指向相同的底层数组。
包含 slice 元素的内存块由 “reference” 传递。
包含容量、元素数量和指向元素的指针的切片信息三元组按 value 传递。
5 小结:
类型 值是否被复制 能否修改原数据 原因
基本类型(int、bool) ✅ 是副本 ❌ 不行 函数内是副本
指针 ✅ 是指针副本 ✅ 可以修改原数据 指针指向原始数据
slice/map/channel ✅ 是结构体副本 ✅ 可以修改原数据 内部默认有底层数据的指针
struct ✅ 整体被复制 ❌/✅ 取决于字段内容 若有指针字段,可能可以改
“引用传递”的说法是为了便于理解,特别是与 Java/C++ 等语言对比的时候。在 Go 中传指针、slice、map 时的行为**“看起来像引用传递”**,因为你修改了函数外的数据。但从语言语义上说,这仍然是 值传递 —— 只不过值中包含的是“引用”
- 点赞
- 收藏
- 关注作者
评论(0)