nil是Go中所有指针类型的零值,不指向任何有效内存地址;直接解引用未初始化的指针会触发panic。声明后不能直接用*p,因nil表示“无目标”而非“空对象”,需先判空再访问,或用new(T)、&x获取非-nil指针。

nil 是 Go 中所有指针类型的零值,它不指向任何有效内存地址;直接解引用未初始化的指针会触发 panic:invalid memory address or nil pointer dereference。
为什么声明后不能直接用 *p?
Go 的指针变量声明即初始化为 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语义不同:前者是空字符串,后者表示“未提供” - 常见于配置解析、API 请求体反序列化等场景,需显式检查
== 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,不能访问字段
- 正确做法:先判空,再访问
最简防御模式:
立即学习“go语言免费学习笔记(深入)”;
if p != nil && p.Name == ""- 或封装为方法:
func (u *User) IsEmpty() bool { return u == nil || u.Name == "" }
零值指针不是“占位符”,它是明确的“未就绪”状态;Go 把这个判断权交给你,而不是替你隐式构造对象。这点在设计 API 接口、配置结构或 ORM 映射时尤其关键——别让 nil 混淆了业务语义。










