<p>Go所有传参均为值传递,但“值”取决于类型底层:基础类型传数据副本,slice/map/chan传含指针的header副本,修改元素会影响原变量;需改变量本身(如重置slice header)时才必须传* T。</p>

Go 里所有传参都是值传递,但“值”是什么得看类型底层
Go 没有引用传递,只有值传递。区别在于:传的是「数据本身」还是「指向数据的结构体副本」。比如 int 传的是整个整数拷贝;而 []int 传的是一个含 ptr、len、cap 的 header 结构体——这个结构体是拷贝的,但它里面的 ptr 仍指向原底层数组。
- 修改
slice元素(如s[0] = 100)会影响原 slice,因为 ptr 没变 - 对
slice做append后若触发扩容,新 slice 的ptr可能指向新地址,原 slice 完全不受影响 -
map和chan类似:传参拷贝的是 header(内部含指针),增删改 key / 发送接收都会作用到同一底层结构
怎么快速判断一个类型传参后改不改原变量
不用背分类表,动手试两行代码最准:
- 写个函数,参数用该类型,函数内修改它(不是改指针,是改内容,比如
arr[0] = 99或m["k"] = 1) - 调用后立刻检查原变量是否变化 —— 变了就是“表现像引用”,没变就是纯值拷贝
- 注意避开陷阱:
string看似像引用(不可变),但其实是只读的值类型;[3]int是值类型,[]int才是引用行为
什么时候必须传 *T 而不是 T
当你需要在函数内修改「变量本身的值」,而不仅是它指向的内容时,就必须传指针。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 想让函数改变调用方的
sliceheader(比如让它指向新数组),必须传*[]int - 想让函数把
map置为nil或换一个新的make(map[string]int),也得传*map[string]int - 结构体很大(比如含大数组字段)时,传
*T避免整块内存拷贝,这是性能刚需,不是语义选择
自定义 struct 是值类型还是引用类型
取决于它有没有指针字段或嵌套引用类型字段。Go 不看名字,看内存布局。
立即学习“go语言免费学习笔记(深入)”;
-
type User struct{ Name string; Age int }→ 值类型,赋值/传参全量拷贝 -
type Config struct{ Data map[string]interface{}; Ch chan int }→ 表现像引用类型,因为字段本身是引用类型,拷贝只是 header - 哪怕 struct 里只有一个
*int字段,它就不再是“纯值语义”:两个 struct 变量可能共享同一堆上 int 的值
最容易被忽略的一点:interface{} 装的是值还是指针,决定了它表现像值还是像引用。装 int 就是值拷贝;装 &v 就是共享同一块内存——这个细节在泛型函数和反射场景里常引发静默 bug。

















