go中无引用传递,全是值传递:传指针即传地址值副本,解引用后可改原内存;必须用* t的两种情况是修改调用方变量值或避免大结构体拷贝。

Go 里没有真正的“引用传递”,所有函数参数都是值传递;所谓“用引用传递”,实际是传指针的值(即地址),然后通过解引用修改原内存位置的数据。
什么时候必须传 *T
只有两种情况真正需要:想修改调用方变量本身的值,或避免拷贝大结构体。
- 修改字段:比如
func (u *User) SetName(n string) { u.name = n }—— 不加*,u是副本,改了白改 - 避免拷贝:一个含
[]byte和嵌套 map 的type Config struct,传Config可能拷贝几 KB,传*Config只拷贝 8 字节地址 - 一致性要求:只要该类型有一个方法用了指针接收者(如
Write),其他方法也建议统一用指针,否则接口实现可能出问题
slice、map、chan 为什么常被误认为“引用传递”
它们传的是 header 或指针的副本,底层数据共享,但 header 自身是否被修改,决定了调用方能否看到变更。
-
slice:传值可改元素(s[0] = 1生效),但s = append(s, x)不生效 —— 因为返回新 header,原变量没变;要就地更新必须传*[]T或显式返回 -
map和chan:增删改查都不改自身指针值,所以传值足够;func (m *StringMap) Set(...)多余,还误导人以为要改 map 变量本身 - 错误现象:
append后长度没变、delete(m, k)却生效 —— 差别就在“改的是底层数组/哈希表”还是“改的是 slice header/map 变量”
常见踩坑点
不是加了 * 就万事大吉,很多 panic 和逻辑错都源于对指针行为的误判。
-
nil指针解引用直接 panic:if p != nil { *p = 42 }必须检查,不能省 - 不能对字面量取地址:
&42、&"hello"、&strings.ToUpper(s)都非法 - 传
*T不等于“能让函数改掉T变量的地址”——你只能改它指向的内容,T变量自身的内存位置永远不变 - 误用
**T:除非真要替换指针变量本身(比如重置一个全局配置指针),否则绝大多数场景不需要二级指针
最易被忽略的是:slice 的行为边界模糊——它既不像纯值(能改底层数组),也不像纯引用(append 后原变量不变)。判断要不要传指针,始终盯住一点:你操作的是 header 字段(ptr/len/cap)还是仅其指向的数据。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











