在 Go 社区常能听到“按值传递”“按引用传递”两种说法:
- 基本类型、数组、结构体被称为“按值”;
- 指针、切片、映射、通道则被称为“按引用”。
然而,上述分类容易造成误解。在 Go 语言中,一切函数实参都以 值 的形式被复制传递。区别仅在于:
- 复制的是“完整数据”(整数、数组等);
- 复制的是“描述符”或指针(切片、映射、字符串等)。
理解这一点后,再看各类示例便能水落石出。
纯值类型:数组、结构体等
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 描述符(数据只读共享) | 无(只读) | 任何写操作 |
结论与建议
- 牢记:Go 只有按值传递。不要再谈“按引用”,最多是“值里装着指针”。
- 修改调用者数据的途径只有两种:
- 传递指针(
*T
、**T
……); - 传递内部含指针的描述符(切片、map、通道)并操作其指向的共享数据。
- 传递指针(
- 重新分配(
append
、make
等)仅改变局部副本头部,不会回写调用方。 - 若你需要让函数“生长”切片或重新绑定 map,使用指针语义:
func f(s *[]T)
或func g(m *map[K]V)
。
正确理解参数传递语义,可避免误判内存行为、消除隐蔽 bug,使代码行为更加可预测。
参考
- Go Class: 04 Strings
- Go Class: 08 Functions, Parameters & Defer
- Go Slices: usage and internals
- Demystifying function parameters in Go
- Go Data Structures
欢迎关注我:
- 知识星球:云原生 AI 实战营,10+ 高质量体系课( Go、云原生、AI Infra)、15+ 实战项目,助你提高技术天花板,入大厂、拿高薪;
- 微信GZH:令飞编程,分享 Go、云原生、AI Infra 相关技术。回复「资料」免费下载 Go、云原生、AI 等学习资料;
- 哔哩哔哩:令飞编程 ,分享技术、职场、面经等,并有免费直播课「云原生AI高新就业课」,大厂级项目实战到大厂面试通关。