能,因传的是指针值副本,仍指向原内存;但仅*x = val修改内容才生效,若x = &y仅改副本指向,原值不变。

为什么传指针有时没改变原值
Go 里函数参数永远是值传递,func f(x *int) 传进去的其实是地址的副本。如果在函数里把 x 指向了新地址(比如 x = &y),外部变量不会受影响;只有通过 *x = ... 修改它指向的内存内容,才会反映到调用方。
常见错误现象:swap 函数里交换两个指针变量本身,而不是它们指向的值,结果原变量没变。
- 想改原值 → 用
*x = newValue - 想换指向 → 外部必须传 **int,且调用方得提供地址的地址
- 结构体大时传
*Struct是惯例,但不是为了“让修改生效”,而是避免拷贝开销
什么时候该传 *T 而不是 T
核心判断依据就两条:是否需要修改原始数据,以及类型大小是否值得避免拷贝。
使用场景举例:
立即学习“go语言免费学习笔记(深入)”;
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 修改 map/slice 的底层数组内容(如
append后要让调用方看到扩容后的 slice)→ 必须传*[]T或用返回值 - 修改结构体字段(比如
user.SetAge(25))→ 方法接收者用func (u *User) SetAge(a int) - 小结构体(如
type Point struct{ X, Y int })传值更高效,没必要加* -
nil检查有意义时(如初始化逻辑),用*T可以区分“未初始化”和“零值”
interface{} 和指针混用的坑
把 &x 传给接受 interface{} 的函数,再在函数里做类型断言,容易掉进“复制后断言”的陷阱。
错误示例:fmt.Printf("%v", &x) 打印的是地址,但如果你写 doSomething(&x),而 doSomething 内部做了 v := interface{}(arg); p := v.(*int),这本身没问题;但如果 arg 是值类型传入再取地址,那地址指向的就是函数栈里的副本。
- 传指针进
interface{}前确认它指向的是调用方堆/栈上有效的变量 - 用
reflect.ValueOf(x).CanAddr()判断能否取地址(调试时有用) - 避免对临时值取地址再转
interface{},比如foo(&struct{A int}{1})是危险的
调试指针问题的实用技巧
Go 没有指针算术,但地址对比和内存布局仍能帮你定位问题。
实操建议:
- 打印地址用
fmt.Printf("%p", &x),对比调用前后是否一致 - 用
unsafe.Sizeof(x)看结构体大小,决定是否值得传指针 - 开启
go run -gcflags="-m"观察编译器是否做了逃逸分析(比如局部变量被返回时自动分配到堆) - 对 slice 传参后怀疑底层数组没更新,直接检查
cap(s)和&s[0]地址是否变化
最常被忽略的一点:指针本身不是魔法,它只是地址。真正决定行为的是你用它做了什么操作——解引用、赋值、还是重新赋值给指针变量。

















