Go语言所有参数均为值传递,传指针即传地址副本;可通过*p修改原值,但重赋值p仅影响副本;需修改原变量、避免大对象拷贝或满足接口实现时才必须用指针。

Go 语言里没有“指针函数”这个语法概念——你写 func() *int,返回的是指向 int 的指针;你写 func(*int),参数是 *int 类型,即接收一个指针。所谓“指针函数传值”,本质是「传指针值」,而 Go 所有参数传递都是值传递,包括指针本身。
为什么 func(p *int) 能修改原变量,但 func(p *int) { p = new(int) } 不行
因为 p 是指针的副本:它和调用方的指针变量存储着相同的地址值,所以通过 *p = 10 可以改到原内存;但如果你给 p 重新赋值(比如 p = new(int)),只改变了这个副本的地址,不影响调用方的指针变量本身。
-
*p = 42→ 修改指针指向的内存内容,有效 -
p = &x或p = new(int)→ 只改本地副本,调用方无感知 - 想让调用方指针变量也变?只能返回新指针,或传入
**int
func(*T) 和 func(T) 在方法接收者中的实际差异
接收者是否用指针,直接决定能否修改结构体字段、是否触发拷贝、是否满足接口实现:
- 用
func(t *MyStruct) Modify():可读可写字段,不拷贝整个结构体,推荐用于大结构体或需修改的场景 - 用
func(t MyStruct) Read():安全只读,但每次调用都复制一份,小结构体(如struct{ x, y int })影响不大 - 接口实现要求一致:如果某个方法用了
*T接收者,那只有*T类型才实现该接口,T类型不满足(哪怕所有方法都存在)
常见错误:var t MyStruct; t.InterfaceMethod() 报错 “t does not implement X: wrong type for Y method”,就是因为接口要求 *MyStruct,而你传了值类型。
立即学习“go语言免费学习笔记(深入)”;
什么时候必须用指针参数,而不是值参数
不是“想改就用指针”,而是看语义和开销:
- 需要修改调用方变量的值 → 必须传
*T,例如json.Unmarshal([]byte, *interface{}) - 类型较大(如含切片、map、大 struct)→ 传
*T避免拷贝,提升性能 - 类型本身包含指针字段(如
sync.Mutex)→ 不能拷贝(sync.Mutex不可拷贝),必须传*T - 统一接收者风格 → 如果结构体已有某个方法用了
*T,其余方法也建议用*T,避免混淆
反例:对 int、string、小 struct 盲目传指针,既没收益,还增加 nil panic 风险。
nil 指针调用方法时的坑
Go 允许对 nil 指针调用方法——只要方法内部没解引用它。这常被用于初始化检查或惰性构造,但极易误判:
-
func (p *MyStruct) Print() { fmt.Println(p.field) }→ 若p == nil,p.fieldpanic -
func (p *MyStruct) Init() { if p == nil { *p = MyStruct{} } }→ 直接 panic,nil 指针不能解引用赋值 - 安全做法:方法开头加
if p == nil { return }或明确文档说明“nil 不接受”
最易忽略的是:方法签名看起来能处理 nil,但实际逻辑隐式依赖非 nil 字段,上线后随机 crash。


















