Go语言所有函数参数都是值传递,包括指针:传的是地址值的副本,而非引用;slice/map/channel是含指针的值类型,故能修改底层数组但扩容不影响原变量。

Go 语言根本没有引用传递
直接说结论:Go 所有函数参数都是值传递,func f(x *int) 传的不是“引用”,而是“地址的副本”。所谓“引用传递”是 C++/Java 等语言的概念,Go 从语法到运行时都不支持。初学者常把 *T 参数行为误称为“引用传递”,其实是混淆了“效果”和“机制”——你确实能修改原值,但底层仍是复制了一个 8 字节的地址值。
指针传递 vs 值传递:看改不改得到原变量
关键区别不在“怎么传”,而在“传的是什么值”:
-
func modifyValue(x int):传的是x的整数值副本,函数内改x = 100,外部a不变 -
func modifyPtr(x *int):传的是&a这个地址值的副本,函数内*x = 100就是在改a本身 - 注意:
x = &b(重赋指针)不会影响调用方的指针变量,因为x是地址副本,不是别名
func reassign(p *int) {
b := 20
p = &b // 只改了副本 p,不影响外面的指针
}
func main() {
a := 10
p := &a
reassign(p)
fmt.Println(*p) // 还是 10,不是 20
}
为什么 slice/map/channel 看起来像“引用传递”?
它们不是引用类型,而是**含指针的值类型**。例如 []int 底层是 struct{ ptr *int, len, cap },传参时复制这个 struct,但 ptr 字段指向同一片底层数组。所以 s[0] = 100 能生效,不是因为“引用”,而是因为复制后的 struct 仍持有原数据地址。
- 切片扩容后(如
append导致底层数组更换),新 slice 的ptr指向新地址,原 slice 不受影响 - 想让函数内扩容影响外部?必须返回新 slice 或传
*[]int -
map和channel同理:内部有指针字段,共享底层数据,但变量本身仍是值传递
什么时候该用指针?三个硬判断条件
别凭感觉,按这三条来:
立即学习“go语言免费学习笔记(深入)”;
- 需要修改调用方的原始值 → 必须用
*T(比如func increment(n *int)) - 结构体字段总大小 > 64 字节(或实测性能差)→ 优先用
*Struct避免拷贝(如含[1000]int的 struct) - 方法接收者需保持一致性 → 如果已有方法用了指针接收者(
func (s *MyStruct) Do()),其他方法也建议统一用指针,避免意外拷贝导致状态不一致 - 反例:小结构体(如
type Point struct{ X, Y int })用值传递更清晰安全,没必要加*
最易被忽略的一点:nil 指针解引用会 panic,而值类型不会;所以用指针前,要么确保非 nil,要么显式判空——这不是风格问题,是运行时安全边界。










