nil是Go中所有指针类型的零值,不指向任何有效内存地址;直接解引用未初始化的指针会触发panic。声明后不能直接用*p,因nil表示“无目标”而非“空对象”,需先判空再访问,或用new(T)、&x获取非-nil指针。
nil 是 Go 中所有指针类型的零值,它不指向任何有效内存地址;直接解引用未初始化的指针会触发 panic:invalid memory address or nil pointer dereference。
*p?nil,但 nil 不代表“空对象”,而是“无目标”。这和 C 的野指针不同——Go 用 panic 主动拦截非法访问,而非静默崩溃或未定义行为。
var p *int → p == nil 成立 p != nil,否则运行时立即中止 new(T) 和 &x 是安全获取非-nil 指针的两种常用方式 package main import "fmt"func main() { var p int // fmt.Println(p) // ❌ panic!
p = new(int) // ✅ 分配 int 零值内存,返回非-nil 指针 fmt.Println(*p) // 输出 0 x := 42 p = &x // ✅ 指向已有变量 *p = 100 fmt.Println(x) // 输出 100}
两者都产生非-nil 指针,但语义与生命周期不同:
new()和&的本质区别
new(T):在堆上分配一块 T 类型的零值内存,返回其地址;适合需要独立生命周期、且无现成变量可取址的场景 &x:获取栈(或堆)上已有变量 x 的地址;不分配新内存,仅建立引用关系 注意:new(int) 返回的指针指向一个刚分配的、初始为 0 的 int;而 &x 的安全性完全依赖 x 的作用域是否还有效(例如不能返回局部变量地址给调用方,除非逃逸分析确认其已分配到堆)。
nil,但容易误判为“已设置”:
*string,"" 和 nil 语义不同:前者是空字符串,后者表示“未提供” 
== nil 而非只看值 type Config struct {
Timeout *int `json:"timeout"`
}
func handleConfig(c Config) {
if c.Timeout == nil {
// 使用默认超时,比如 30 秒
fmt.Println("timeout not set, using default")
} else {
fmt.Printf("timeout set to %d seconds\n", *c.Timeout)
}
}
var p *User 等价于一个空用户”,实际它连内存都没分配
- 错误写法:if p.Name == "" —— panic!因为 p 是 nil,不能访问字段
- 正确做法:先判空,再访问
最简防御模式:
if p != nil && p.Name == ""func (u *User) IsEmpty() bool { return u == nil || u.Name == "" }
零值指针不是“占位符”,它是明确的“未就绪”状态;Go 把这个判断权交给你,而不是替你隐式构造对象。这点在设计 API 接口、配置结构或 ORM 映射时尤其关键——别让 nil 混淆了业务语义。