Golang中的指针与值类型差异_Golang指针与值传递的选择与优化

4次阅读

go默认值传递,修改参数副本不影响原变量;需传指针才能写回原值;大Struct、需修改或避免拷贝时用指针;slice/map/chan本身是引用类型,传值即可修改底层数组,除非要改其header才需指针。

Golang中的指针与值类型差异_Golang指针与值传递的选择与优化

为什么修改函数参数时,有些改动不生效?

因为 Go 默认是值传递:传入函数的是变量的副本,对副本的修改不会影响原始变量。只有传指针(&v)才能让函数内操作反映到原变量上。

常见错误现象:func modify(s String) { s = "new" } 调用后原字符串不变;而 func modify(s *string) { *s = "new" } 才能改成功。

  • 值类型intstringstruct 等)小对象传值开销小,且天然线程安全
  • 大 struct(比如含多个 slice 或大数组)传值会拷贝大量内存,此时应传 *T
  • 需要函数“写回”数据时,必须用指针——比如 json.Unmarshal 第二个参数强制要求 *T

接收者用值还是指针?看是否要修改或避免拷贝

方法接收者类型直接影响能否修改原值,也影响调用时的性能和接口实现行为。

  • 如果方法要修改接收者字段,必须用指针接收者(func (p *Person) SetName(n string)
  • 如果类型是大 struct,即使只读方法也建议用指针接收者,避免每次调用都拷贝
  • 同一类型混用值/指针接收者会导致接口实现不一致:比如 Stringer 接口被值接收者实现,则 *T 类型不自动满足该接口
  • 标准库惯例:所有可变方法(如 bytes.Buffer.Write)都用指针接收者

什么时候不该用指针?小心逃逸和 GC 压力

不是所有地方都适合加 *。过度使用指针可能把本该在上分配的小变量推到上,触发额外 GC。

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

  • 小基础类型(intbool、小 struct)传值比传指针更快,CPU 缓存更友好
  • go tool compile -gcflags "-m" main.go 可查看变量是否逃逸;若提示 ... moved to heap,就要评估是否真需指针
  • 闭包中捕获局部变量时,若用指针引用它,该变量必然逃逸——哪怕它原本很小
  • map 的 value 是指针类型(如 map[string]*User)时,注意 key 不存在时取值为 nil,解引用前必须判空

切片、map、channel 本身已是引用类型,别画蛇添足传指针

它们底层结构包含指针字段(如 slice 有 data 指针),所以传值即可修改底层数组内容,无需再套一层 *[]T

  • func appendItem(s []int, x int) []int 正确;func appendItem(s *[]int, x int) 多余且易错
  • 但若想修改 slice header 本身(比如扩容后让调用方看到新 header),才需要 *[]T
  • 同理,mapchan 作为参数传值即可,除非你要替换整个 map 变量(如 m = make(map[int]string)),那才需 *map[K]V
  • 误传 *map 还可能导致 panic:对 nil *map 解引用后赋值,会 panic,而直接传 map 不会

指针不是优化银弹。关键在分清“要不要改原值”和“值本身有多大”。小值传值,大值或需修改时传指针,引用类型(slice/map/chan)按需决定是否要改 header。逃逸分析和实际 profile 比直觉更可靠。

text=ZqhQzanResources