bigywater.tech
常见报错 · 44

Go panic: invalid memory address or nil pointer dereference:addr=0x18 是字段偏移,typed nil 让 err != nil 为 true,十种“空”只有三种不出错

nil pointer dereference 是 Go 里最常见的 panic,常见建议是“多判几个 nil”。这篇在本机把它拆开:报错里的 addr 其实告诉你读的是哪个字段,同样叫“空”的十种情形各发生什么,typed nil 为什么让 err != nil 为真,三个线上最常见的来源,以及怎么保住位置信息。

0x18
读 nil *User 的 Email 字段时 SIGSEGV 的 addr:正好是字段偏移(string 16 字节 + int 8 字节)
10 种
各种“nil”情形里只有读 nil map、nil slice append 和不碰字段的 nil 接收者不出错
true
typed nil 作为 error 返回时 err != nil 的值

典型报错

实测 · 本机执行输出
  读 nil 指针的字段(退出码 2,用时 0.11 s):
      准备读 u.Email
      panic: runtime error: invalid memory address or nil pointer dereference
      [signal SIGSEGV: segmentation violation code=0x2 addr=0x18 pc=0x102317594]
      goroutine 1 [running]:
      main.main()
      	<tmp>/basic/main.go:8 +0x54
  加 recover(退出码 0,用时 0.11 s):
      recover: runtime error: invalid memory address or nil pointer dereference | runtime.Error: true

三部分信息,是 panic 可 recover,抓到的是 runtime.Error。

addr 的含义

实测 · 本机执行输出
  访问 t.A(偏移 0):[signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x100901dd0]
  访问 t.B(偏移 8):[signal SIGSEGV: segmentation violation code=0x2 addr=0x8 pc=0x1024bddac]
  访问 t.C(偏移 16):[signal SIGSEGV: segmentation violation code=0x2 addr=0x10 pc=0x102861d88]
  访问 b.X(偏移 1 MiB):[signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x104b5de18]

addr 是字段偏移,可以反推是哪个字段;偏移很大时变回 0x0。

十种 nil

实测 · 本机执行输出
  写 nil map: m["a"]=1:退出码 2,首行:panic: assignment to entry in nil map
  读 nil map: m["a"]:退出码 0,首行:读到 0 0
  nil slice append / len / range:退出码 0,首行:append 后 [1] 1
  下标访问 nil slice: s[0]:退出码 2,首行:panic: runtime error: index out of range [0] with length 0
  调用 nil 函数变量:退出码 2,首行:panic: runtime error: invalid memory address or nil pointer dereference
  调用 nil interface 的方法:退出码 2,首行:panic: runtime error: invalid memory address or nil pointer dereference
  指针接收者方法里不碰字段(nil 接收者):退出码 0,首行:返回 nil 接收者,没出错
  值接收者方法用 nil 指针调用:退出码 2,首行:panic: runtime error: invalid memory address or nil pointer dereference
  指针接收者方法里读字段:退出码 2,首行:panic: runtime error: invalid memory address or nil pointer dereference
  读 nil 指针的 map 字段: t.M["a"]:退出码 2,首行:panic: runtime error: invalid memory address or nil pointer dereference

读 nil map 不错,写 nil map 另一句 panic;nil 接收者不碰字段也不错。

typed nil

实测 · 本机执行输出
  typed nil(退出码 0,用时 0.32 s):
      bad():  err != nil 为 true,类型 *main.MyErr,值 true
      good(): err != nil 为 false,类型 <nil>
      进入 if 分支,准备 err.Error()
      recover: runtime error: invalid memory address or nil pointer dereference

返回有类型的 nil 指针使 err != nil 为真,没有错就 return nil。

常见来源

实测 · 本机执行输出
  http.Get 失败后直接 defer resp.Body.Close()(退出码 2,用时 0.37 s):
      err = true
      panic: runtime error: invalid memory address or nil pointer dereference
      [signal SIGSEGV: segmentation violation code=0x2 addr=0x40 pc=0x100d6a1bc]
      goroutine 1 [running]:
      main.main()
  map[string]*User 取不存在的 key 再 .Name(退出码 2,用时 0.35 s):
      alice
      panic: runtime error: invalid memory address or nil pointer dereference
      [signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x104fdf694]
      goroutine 1 [running]:
      main.main()
  json 里缺了可选的嵌套对象(退出码 2,用时 0.33 s):
      Retry == nil: true
      panic: runtime error: invalid memory address or nil pointer dereference
      [signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x100348230]
      goroutine 1 [running]:
      main.main()

忘判 err、map 取不到、JSON 缺可选对象。

定位

实测 · 本机执行输出
  recover + debug.Stack(退出码 0,用时 0.31 s):
      recover 只有: runtime error: invalid memory address or nil pointer dereference
      debug.Stack 里: <tmp>/stack/main.go:12 +0x5c
      debug.Stack 里: main.level3(...)
      debug.Stack 里: <tmp>/stack/main.go:5
      debug.Stack 里: main.level2(...)
      debug.Stack 里: <tmp>/stack/main.go:6
      debug.Stack 里: main.level1(...)
      debug.Stack 里: <tmp>/stack/main.go:7
      …(共 9 行)

recover 里用 debug.Stack() 保留栈,只打印值没有位置。

分步演示

nil 指针
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64;报错文本都是真实复现时原样捕获的(命令行工具统一用 LC_ALL=C 取英文原文,路径换成 );所有服务都是本机上自己起的、只监听 127.0.0.1,不访问外网;计时用 time.perf_counter,数字每次运行会有小幅抖动。本篇用 go1.27.3 darwin/arm64;每个程序是 go build 后直接运行的小 main.go;栈里的十六进制地址(pc)每次运行不同,已不当作结论
    • 没有验证:Linux 与 Windows 上 SIGSEGV 行里 code 和 addr 的写法(这里 macOS 是 code=0x2);为什么大偏移时 addr 变成 0x0(结论来自现象:编译器改成显式检查,没有看编译器源码);nil 检查对性能的影响;静态检查工具(nilaway、staticcheck)的效果;GOTRACEBACK 其它取值
    京ICP备2026067502号-1京公网安备11010502063487号