Go 值传递、引用传递与切片详解
值传递
在值传递中,函数接收参数值的一个副本。这意味着在函数内部对参数的任何修改都不会影响到函数外部的原始数据。值传递通常更安全,因为它保证了函数不会意外地修改外部数据。但是,值传递可能效率较低,特别是当传递大对象或数组时,因为需要复制整个对象。
package main
import (
"fmt"
)
func increment(num int) int {
num = num + 1
return num
}
func main() {
count := 5
count = increment(count) // 需要将结果重新赋值给 count
fmt.Println(count) // 输出 6
}
引用传递
在引用传递中,函数接收参数的引用而非实际数据。这意味着在函数内部对参数的任何修改都会影响到函数外部的原始数据。引用传递通常更高效,因为它避免了复制数据,这在处理大对象或数组时特别有用。但是,引用传递可能会导致难以调试的错误,因为函数可能会意外地修改外部数据。
package main
import (
"fmt"
)
func increment(numPtr *int) {
*numPtr = *numPtr + 1
}
func main() {
count := 5
increment(&count) // 传递 count 的指针
fmt.Println(count) // 输出 6
}
注意:&count
获取变量 count
的地址,*numPtr
对指针进行解引用,以获取指针指向的实际值。
slice 的组成
在 Go 语言中,slice
是对数组的抽象,它由以下三个部分组成:
指针
指向底层数组中 slice
起始位置的指针。
长度(len)
slice
的长度,表示它包含的元素数量,即从 slice
的开始位置到结束位置的元素个数。
容量(cap)
slice
的容量,表示从 slice
的开始位置到底层数组末尾的元素数量。
slice 的存储方式
slice
在底层是一个结构体,包含了上述的指针、长度和容量。它的指针指向底层数组的一段连续内存空间。通过 len
可以确定 slice
目前有多少元素,通过 cap
可以知道在不扩容的情况下还可以存储多少元素。
当向 slice
添加元素且超过其容量时,Go 会自动分配一个更大的底层数组,并将现有的元素复制到新数组中。
package main
import "fmt"
func main() {
// 创建一个底层数组
numbers := [...]int{1, 2, 3, 4, 5}
// 创建一个 slice,引用数组的第 2 到第 4 个元素
subSlice := numbers[1:4]
fmt.Println(subSlice) // 输出 [2 3 4]
fmt.Println(len(subSlice)) // 输出 3
fmt.Println(cap(subSlice)) // 输出 4
// 向 slice 添加元素
subSlice = append(subSlice, 6)
fmt.Println(subSlice) // 输出 [2 3 4 6]
fmt.Println(len(subSlice)) // 输出 4
fmt.Println(cap(subSlice)) // 输出 4
}
为什么需要容量(cap)
cap
函数用于返回 slice
的容量,即从 slice
的起始位置到底层数组末尾的元素数量。容量的概念很重要,因为它影响了 slice
的性能,特别是在添加新元素时。
当向 slice
添加元素并超过其容量时,Go 会分配一个新的底层数组,并将现有元素复制到新数组中。这个过程会影响性能,尤其是对于大型 slice
。了解 slice
的容量,有助于编写性能更佳的代码。
package main
import "fmt"
func main() {
// 创建一个初始容量为 3 的 slice
numbers := make([]int, 3)
numbers[0] = 1
numbers[1] = 2
numbers[2] = 3
fmt.Println(numbers) // 输出 [1 2 3]
fmt.Println(len(numbers)) // 输出 3
fmt.Println(cap(numbers)) // 输出 3
// 追加元素,超过容量,触发扩容
numbers = append(numbers, 4)
fmt.Println(numbers) // 输出 [1 2 3 4]
fmt.Println(len(numbers)) // 输出 4
fmt.Println(cap(numbers)) // 输出 6 或更大,取决于扩容策略
}
提示:合理地设置 slice
的容量,可以减少扩容次数,提升性能。