Golang中值参数的实现与探讨
Golang中值参数的实现与探讨
func foo(someSlice []string){
//some processiong here
// just read operations on `someSlice`
}
func main() {
hugeSlice := []string{"one","two",..., "latestValue"}
foo(hugeSlice);
}
我们知道,在 Go 语言中函数参数默认是通过数据副本传递的,那么如果原始的 hugeSlice 分配了 10MB 内存,我们将其传递给函数 foo,这是否意味着在处理 foo 时会在内存中创建 hugeSlice 的副本,并且 foo 内部的所有操作都将基于该副本数据而不是原始切片进行处理?
其次,这是否意味着在处理 foo 时总内存分配至少约为 20MB,因为创建了 hugeSlice 的副本?
更多关于Golang中值参数的实现与探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html
不,切片被复制了,但底层数组没有被复制。请看这个:https://goplay.space/#hK7qi5Czt7Z
%p 打印底层数组的地址
切片只是一个指向数组的指针,这个数组有多长(容量)以及数组使用了多少(长度)。在这里阅读更多相关信息:https://blog.golang.org/go-slices-usage-and-internals
更多关于Golang中值参数的实现与探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go语言中,切片(slice)作为函数参数传递时,实际上传递的是切片头(slice header)的副本,而不是底层数组的副本。切片头包含三个字段:指向底层数组的指针、长度和容量。
对于你的代码示例:
func foo(someSlice []string) {
// 这里的操作只读取someSlice
for i := range someSlice {
_ = someSlice[i] // 只读操作
}
}
func main() {
hugeSlice := []string{"one", "two", ..., "latestValue"}
foo(hugeSlice)
}
内存分配分析:
-
不会复制底层数组:当
hugeSlice传递给foo时,只会复制切片头(约24字节,在64位系统上),底层数组仍然是同一个。 -
内存使用:总内存分配仍然是约10MB,不会增加到20MB。两个切片共享同一个底层数组。
验证示例:
package main
import "fmt"
func foo(someSlice []string) {
// 修改切片内容会影响原始切片
if len(someSlice) > 0 {
someSlice[0] = "modified" // 这会修改原始数据
}
}
func main() {
hugeSlice := []string{"one", "two", "three"}
fmt.Println("Before:", hugeSlice) // [one two three]
foo(hugeSlice)
fmt.Println("After:", hugeSlice) // [modified two three]
}
如果确实需要副本的情况:
func foo(someSlice []string) {
// 如果需要独立副本,需要显式复制
copySlice := make([]string, len(someSlice))
copy(copySlice, someSlice)
// 现在对copySlice的操作不会影响原始切片
copySlice[0] = "safe modification"
}
func bar(someSlice []string) {
// 或者使用切片表达式创建副本
clonedSlice := append([]string(nil), someSlice...)
clonedSlice[0] = "another safe modification"
}
关键点总结:
- 切片传递只复制切片头,不复制底层数组
- 函数内对切片元素的修改会影响原始数据
- 总内存分配不会因为函数调用而翻倍
- 如果需要独立副本,需要显式使用
copy()或append()创建新切片

