终极真相:Go 中的参数传递

在 Go 社区常能听到“按值传递”“按引用传递”两种说法:

  • 基本类型、数组、结构体被称为“按值”;
  • 指针、切片、映射、通道则被称为“按引用”。

然而,上述分类容易造成误解。在 Go 语言中,一切函数实参都以 的形式被复制传递。区别仅在于:

  1. 复制的是“完整数据”(整数、数组等);
  2. 复制的是“描述符”或指针(切片、映射、字符串等)。

理解这一点后,再看各类示例便能水落石出。

纯值类型:数组、结构体等

func do(b [3]int) int {
    b[0] = 0         // 只修改副本
    return b[1]
}

func main() {
    a := [3]int{1, 2, 3}
    v := do(a)       // 传参时完整复制
    fmt.Println(a, v) // [1 2 3] 2
}
  • a 在调用处被整体复制后传入 do
  • do 内部的任何修改都作用于该副本,原始数组保持不变。
  • 若想在被调函数内部改动调用者的数组,需改为 *[3]int

描述符类型:映射(map)

func do(m map[int]int) {
    m[3] = 1
    m[4] = 3
}

func main() {
    m := map[int]int{4: 2}
    do(m)                      // 仅复制 map 头部(指针)
    fmt.Println(m)             // map[3:1 4:3]
}
  • 传入的 是一个指向运行时哈希表的指针副本
  • 副本与原指针指向同一底层数据,因此键值修改对调用者可见;
  • 若在函数内执行 m = make(map[int]int),只会修改本地副本,不影响调用者。

切片:三字节描述符的特殊性

func do(s []int) int {
    s = append(s, 4) // 可能触发重新分配
    s[0] = 0
    return s[1]
}

func main() {
    a := []int{1, 2, 3}         // len=3 cap=3
    v := do(a)                  // 复制切片头部
    fmt.Println(a, v)           // [1 2 3] 2
}
  • 切片头部 = 指针 + 长度 + 容量。
  • append 时容量不足会 分配新数组 并返回新的切片头部;
  • 该新头部仅存在于 do 中,调用者仍指向旧数组,因此 a[0] 未被修改。

若确需影响调用方切片的长度/容量,可显式传入 *[]T

func do(s *[]int) {
    *s = append(*s, 4) // 直接改写调用者变量
    (*s)[0] = 0
}

参数永远“不是别名”

Go 永远复制实参,将其存入被调函数栈帧;函数内变量 绝不是调用方变量的别名
想要修改调用者的数据:就传递能“间接定位”到它的东西(指针或接口值的内部指针)。

类型传递时被复制的内容何时能改到调用方数据何时改不到
基本类型整个值总改不到
数组整个数组总改不到
结构体整个结构体总改不到
指针指针本身解引用后可改修改指针本身
切片指针+len+cap 描述符修改底层数组元素重新分配、改头部
映射指向哈希表的指针改键值对重新 make
通道指向 channel 结构的指针发/收消息重新 make
字符串指针+len 描述符(数据只读共享)无(只读)任何写操作

结论与建议

  1. 牢记:Go 只有按值传递。不要再谈“按引用”,最多是“值里装着指针”。
  2. 修改调用者数据的途径只有两种:
    • 传递指针(*T**T……);
    • 传递内部含指针的描述符(切片、map、通道)并操作其指向的共享数据。
  3. 重新分配(appendmake 等)仅改变局部副本头部,不会回写调用方。
  4. 若你需要让函数“生长”切片或重新绑定 map,使用指针语义:func f(s *[]T)func g(m *map[K]V)

正确理解参数传递语义,可避免误判内存行为、消除隐蔽 bug,使代码行为更加可预测。

参考

欢迎关注我:

  • 知识星球:云原生 AI 实战营,10+ 高质量体系课( Go、云原生、AI Infra)、15+ 实战项目,助你提高技术天花板,入大厂、拿高薪;
  • 微信GZH:令飞编程,分享 Go、云原生、AI Infra 相关技术。回复「资料」免费下载 Go、云原生、AI 等学习资料;
  • 哔哩哔哩:令飞编程 ,分享技术、职场、面经等,并有免费直播课「云原生AI高新就业课」,大厂级项目实战到大厂面试通关。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值