Golang指针的传递与解引用_Golang指针传递与解引用优化方法

10次阅读

go函数传参只有值传递,T传的是地址副本;修改p影响原值,但p重指向不影响外部指针;判断传*T还是T需综合考虑是否需修改原值、结构体大小及是否含不可拷贝字段。

Golang指针的传递与解引用_Golang指针传递与解引用优化方法

Go 函数传参时指针到底传了什么

Go 里没有“引用传递”,只有值传递。传 *T 类型参数时,传递的是指针变量的副本——也就是一个内存地址的拷贝。这个副本和原指针指向同一块/内存,所以修改 *p 能影响原值;但若在函数内让 p 指向别处(比如 p = &x),对外部原指针无任何影响。

常见误判场景:

  • 以为 func foo(p *int) { p = new(int) } 能改变调用方的指针变量本身 → 实际不能
  • 对小结构体盲目加 * 传参,反而增加一次内存寻址开销
  • 在 defer 中解引用已释放的指针(如循环中取切片元素地址后延迟打印)→ 可能 panic 或读到脏数据

什么时候该传 *T,什么时候该传 T

判断核心不是“要不要改内容”,而是“改不改得动”+“值有多大”:

  • 需要修改调用方变量所指向的值 → 必须传 *T,例如 json.Unmarshal 第二个参数是 Interface{},但实际常传 &v
  • T 是大结构体(比如超过 24 字节)→ 传 *T 避免复制开销;但要注意:如果函数只读、且编译器能逃逸分析出栈分配,传值可能更快
  • Tsync.Mutexhttp.Client 等含不可拷贝字段的类型 → 只能传 *T,否则编译报错 cannot be copied
  • 返回值是 *T 但后续只读 → 考虑是否真需暴露指针,避免调用方意外修改内部状态

*T 解引用时 panic 的典型原因

最常见的是 nil 指针解引用:panic: runtime Error: invalid memory address or nil pointer dereference。它不一定发生在 *p 这一刻,而可能在调用 p.Method() 时触发(方法集隐式解引用)。

立即学习go语言免费学习笔记(深入)”;

高频出错点:

  • map 中存 *T,但 key 不存在时直接 val := m[key]; *valval 是零值 nil
  • 接口变量底层是 *T,但未做非空检查就断言并解引用:if v, ok := i.(*MyStruct); ok { use(*v) } → 若 inil 接口,vnil 指针,*v panic
  • 使用 new(T) 后忘记初始化字段,直接访问嵌套指针字段:p = new(Conf); p.DB.Addrp.DB 是 nil

逃逸分析对指针行为的实际影响

Go 编译器会决定变量分配在栈还是堆,这直接影响指针的有效性。用 go build -gcflags="-m" 可查看:

例如:

func getPtr() *int {     x := 42     return &x // x 逃逸到堆,否则返回栈地址非法 }

但以下不会逃逸:

func sum(a, b int) int {     p := &a     return *p + b // p 在栈上,&a 不逃逸,因为没被返回或存储到全局

关键提醒:

  • 不要依赖“局部变量一定在栈上”来写指针逻辑,逃逸规则会随 Go 版本微调
  • benchmark 中发现指针版本更慢?先看是否因逃逸导致堆分配增多、GC 压力上升
  • 结构体字段含指针(如 type S struct{ data *[]byte })易引发意外逃逸,应尽量扁平化或用 slice 替代

text=ZqhQzanResources