必须用 *T 而不是 T 才能修改调用方的指针变量本身(如重定向或置 nil);*T 只能修改其指向的值,因 Go 参数均为值传递。

什么时候必须用 **T 而不是 *T
只有当你需要在函数内部**修改调用方持有的指针变量本身**(比如让它指向新地址、置为 nil),才真正需要 **T。传 *T 只能改它指向的值,改不了指针自己——因为 Go 里所有参数都是值传递。
- 错误写法:
func setToFive(p *int) { p = &five }→ 外部ptr不变 - 正确写法:
func setToFive(pp **int) { *pp = &five }→ 传&ptr,*pp就是原ptr变量 - 典型场景:惰性初始化、全局配置热替换、链表头节点更新、CGO 中接收 C 分配的内存(如
C.get_string(&cPtr))
***T 是否合法?为什么几乎不用
***T 在语法上完全合法,Go 类型系统允许任意嵌套,但它不是“多级指针语法糖”,而是明确指向一个 **T 变量的地址。实际项目中极少出现,一旦看到,大概率说明设计可以简化。
- 三层解引用需三次
*:若ppp是***int,取值必须写***ppp,少一次类型错,多一次 panic - 空指针检查成本陡增:
if ppp != nil && *ppp != nil && **ppp != nil才敢访问 - 替代方案更清晰:用结构体封装(如
type IntRef struct { Ptr **int })、返回新指针、或用sync/atomic.Value安全替换
安全访问 **T 的硬性操作步骤
任何对 **T 的解引用都必须前置非空校验,Go 不做隐式防护,nil 解引用直接 panic。
- 声明后不能直接用:
var pp **int→*pp会 panic - 逐层初始化是底线:
val := 42 p := &val // *int pp := &p // **int
- 解引用前必判:
if pp != nil && *pp != nil { use **pp },顺序不可颠倒(&&短路)
常见 panic 错误和调试线索
运行时报 panic: runtime error: invalid memory address or nil pointer dereference 是多级指针最常触发的错误,根源几乎总是某一层未初始化或提前被置 nil。
立即学习“go语言免费学习笔记(深入)”;
- 高频错误:
var pp **int; *pp = &x→pp是nil,*pp就非法 - 调试技巧:打印每层值:
fmt.Printf("pp=%v, *pp=%v\n", pp, *pp),快速定位哪一层断了 - CGO 场景特别注意:C 函数可能不填参(
cPtr保持nil),Go 层必须先检查再解引用
**T 拆成“返回新指针 + 显式赋值”或封装进方法,代码反而更直白、更易测、更少出错。










