Go中是否用指针取决于四点:是否需修改原值、值大小、接口实现需求及并发共享需要;传值不改原值且小结构体更高效,大结构体、需修改、实现接口或并发时应传指针。

Go中是否用指针,核心看两点:要不要修改原值,以及值是否太大。不是所有结构体都要传指针,也不是所有基础类型都不能传指针——关键在语义和效率。
需要修改原始数据时必须用指针
函数内对参数的修改要反映到调用方,就必须传指针。基础类型(int、string、struct等)默认传值,改了也白改。
- 比如有一个User结构体,想在函数里改它的Name字段,传值进去,函数内改完,外面还是老样子;传*User,就能改成功
- 切片虽是引用类型,但底层数组指针+长度+容量是按值传递的。所以能改元素,但不能让调用方的切片变量指向新底层数组(比如做扩容后重新赋值),这时也需要指针(*[]T)或返回新切片
大结构体建议传指针避免拷贝开销
Go每次传参都复制一份值。如果结构体字段多、嵌套深、含大数组或大slice,拷贝成本明显。一般超过16–32字节就值得考虑指针。
- 例如:type BigData struct { ID int; Payload [1024]byte; Meta map[string]string },每次传值会复制1KB+,明显浪费
- 但像type Point struct{ X, Y int }(通常16字节以内),传值反而更轻量,且更安全、更易内联
方法集与接口实现常依赖指针接收者
一个类型T的方法集只包含“T接收者”的方法;*T的方法集则包含“T接收者”和“*T接收者”的全部方法。而接口变量要能存某个类型,该类型必须实现接口所有方法——这常迫使你用指针。
- 比如接口type Writer interface{ Write([]byte) (int, error) },如果你给type MyWriter struct{...}定义的是func (w *MyWriter) Write(...),那只有*MyWriter才满足Writer,MyWriter{}字面量直接传就会报错
- 常见陷阱:用var w MyWriter然后fmt.Fprint(w, "hi")失败,因为fmt.Stringer要求String()方法,而你只实现了*MyWriter.String()
并发安全与共享状态天然倾向指针
多个goroutine要读写同一份数据,必须共享内存地址,也就是用指针(或通过channel传递指针)。值拷贝意味着各玩各的,起不到同步作用。
- 典型如sync.Mutex,必须取地址:var mu sync.Mutex; mu.Lock()合法,但func f(m sync.Mutex) { m.Lock() }锁的是副本,完全无效
- 类似地,sync/atomic操作、unsafe.Pointer转换、CGO传参等底层场景,几乎都强制要求指针
基本上就这些。不复杂但容易忽略:先想“我要不要改它”,再看“它大不大”,接着检查“接口能不能接住”,最后想想“有没有并发读写”。四步下来,指针用不用,心里就有数了。









