如何使用Golang的指针传递数据_Golang指针传递与引用类型

1次阅读

go只有值传递指针传递,所谓“引用类型”如slice、map本质是含指针的结构体,传参仍是值传递;修改基础类型或避免大结构体拷贝等场景必须用*T。

如何使用Golang的指针传递数据_Golang指针传递与引用类型

Go 里没有“引用传递”,只有值传递和指针传递

Go 语言中不存在 c++ 那种语法层面的引用类型(&T 作为独立类型),所有函数参数都是值传递。所谓“引用类型”(如 slicemapchanfunc*T)只是底层数据结构包含指针字段,使得它们在传参时能“看起来像”可修改原数据——但这本质仍是值传递一个含指针的结构体。

真正想修改调用方变量的值,必须显式传入指针:*T。比如想让函数改掉一个 int 变量,不传 *int 就永远改不了。

什么时候必须用 *T 传参

以下情况需传指针,否则无法影响原始变量:

  • 修改基础类型(intStringStruct 等)的字段或值
  • 避免大结构体拷贝(例如几百字节以上的 struct),提升性能
  • 实现接口时,方法集与接收者类型强相关:带指针接收者的方法只能被 *T 调用

示例:

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

func increment(x *int) {     *x++ } n := 42 increment(&n) // 必须取地址;传 n 无效 // n 现在是 43

slicemap 为什么“不用指针也能改”

因为它们本身是头信息结构体,内部含指针字段:

  • slicestruct{ ptr *T; len, cap int } —— 传参复制的是这个结构体,但 ptr 指向同一底层数组
  • map*hmap(即指针类型),所以传 map[K]V 实际上传的是指针的副本

注意:这不等于“安全”。以下操作仍不会影响原变量:

  • slice 重新赋值(s = append(s, x) 可能导致扩容,生成新底层数组)
  • map 重新赋值(m = make(map[int]string) 只改副本)

若需保证扩容后仍影响原 slice,必须传 *[]T;map 同理需 *map[K]V(极少用,通常说明设计有问题)。

常见误用和坑

容易混淆的点集中在“什么变了、什么没变”:

  • struct{ name string; age int } → 修改字段无效;传 *struct{...} → 有效
  • []byte → 可改元素、可 append(但扩容后原 slice 不变);传 *[]byte → 连底层数组更换都能同步
  • nil 指针解引用会 panic:var p *int; *p = 1 → crash。使用前务必检查是否为 nil
  • 返回局部变量地址安全(Go 编译器自动逃逸分析),但返回局部数组的地址要小心:如 return &[3]int{1,2,3} 安全,arr := [3]int{1,2,3}; return &arr 也安全(逃逸),但语义易误导

最常被忽略的是:函数签名是否匹配方法集。比如定义了 func (t *User) Save(),却用 User{} 值调用,会报错 cannot call pointer method on ...

text=ZqhQzanResources