Golang中的指针与引用_Golang指针的定义与使用场景

2次阅读

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

Golang中的指针与引用_Golang指针的定义与使用场景

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())只能读或改副本
  • 切片mapchannel 本身是引用类型(底层含指针),但它们的变量仍是值——所以 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 都是干净实例

指针本身不难写,难的是谁拥有它、何时失效、是否并发安全——这些都得靠代码约定和审查,编译器不会帮你拦住。

text=ZqhQzanResources