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

2 回复

不,切片被复制了,但底层数组没有被复制。请看这个: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)
}

内存分配分析:

  1. 不会复制底层数组:当hugeSlice传递给foo时,只会复制切片头(约24字节,在64位系统上),底层数组仍然是同一个。

  2. 内存使用:总内存分配仍然是约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()创建新切片
回到顶部