Go中无引用类型,只有指针;&操作符返回指针,用于解引用;“传引用”实为误称,修改原变量需显式传 T并解引用赋值。

Go 里没有引用类型,只有指针
Go 语言中不存在 C++ 那种语法层面的「引用(reference)」,& 操作符永远返回一个指针,* 永远用于解引用。所谓「传引用」只是开发者对「传指针」行为的误称。函数参数若想修改原始变量,必须显式传入 *T 类型,并在函数内用 *p = ... 赋值。
常见错误现象:func modify(x int) { x = 42 } 不会改变调用方的变量;而 func modify(x *int) { *x = 42 } 才会。
- 结构体较大时,传
*S比传S更节省内存和复制开销 - 方法接收者用指针(
func (s *S) Method())才能修改字段;值接收者(func (s S) Method())只能读或改副本 - 切片、map、channel 本身是引用类型(底层含指针),但它们的变量仍是值——所以
append后需重新赋值,否则调用方看不到新元素
new() 和 & 的区别:何时该用哪个
new(T) 返回 *T,且将内存置零;&t 取已有变量 t 的地址。二者语义不同,不能混用。
使用场景:
立即学习“go语言免费学习笔记(深入)”;
- 需要一个零值指针且无现成变量时,用
new(T)—— 如ptr := new(int)等价于var tmp int; ptr := &tmp - 已有变量要传给函数或赋给结构体字段,直接用
&v—— 更直观、更常见 -
new([]int)返回*[]int(指向 nil 切片的指针),不是你想要的可 append 的切片;此时应写&[]int{}[0]或更稳妥地用make+&
nil 指针解引用 panic 的典型场景
Go 运行时会在解引用 nil 指针时 panic,错误信息为 panic: runtime error: invalid memory address or nil pointer dereference。这不是编译错误,容易漏测。
常见触发点:
- 函数返回
*T,但某些分支没初始化就直接 return,调用方未判空就*p - 结构体字段是
*T,初始化时忘记赋值,后续访问s.field.X前没检查s.field != nil - 用
json.Unmarshal解析到*T字段,JSON 中该字段缺失或为null,结果字段为nil,后续直接解引用
建议:所有对外暴露的 *T 参数、返回值、字段,文档或注释里明确是否允许 nil;关键路径上加 if p == nil { return ... }。
sync.Pool 与指针生命周期的隐含风险
sync.Pool 缓存的是任意接口值,如果缓存了 *T,要注意其指向的内存可能已被回收或复用。虽然 Go 的 GC 会追踪指针,但 Pool 的「放回」不等于「安全持有」。
典型问题:
- 从 Pool 获取
*T后,修改其字段,再放回;下次取出来时内容可能被污染(因 Pool 不清零) - 结构体中有
*string或*[]byte字段,Pool 放回后,这些子指针仍指向旧数据,导致意外共享 - 正确做法:从 Pool 获取后,显式重置字段(如
p.Field = "");或改用sync.Pool{New: func() interface{} { return &T{} }}确保每次 New 都是干净实例
指针本身不难写,难的是谁拥有它、何时失效、是否并发安全——这些都得靠代码约定和审查,编译器不会帮你拦住。










