惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - Franky
C
CXSECURITY Database RSS Feed - CXSecurity.com
S
Schneier on Security
Know Your Adversary
Know Your Adversary
Security Latest
Security Latest
Spread Privacy
Spread Privacy
Project Zero
Project Zero
T
The Exploit Database - CXSecurity.com
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
AI
AI
N
News | PayPal Newsroom
A
Arctic Wolf
NISL@THU
NISL@THU
W
WeLiveSecurity
Security Archives - TechRepublic
Security Archives - TechRepublic
Hacker News: Ask HN
Hacker News: Ask HN
P
Palo Alto Networks Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
大猫的无限游戏
大猫的无限游戏
L
Lohrmann on Cybersecurity
Last Week in AI
Last Week in AI
T
Threatpost
The Last Watchdog
The Last Watchdog
博客园_首页
C
Cybersecurity and Infrastructure Security Agency CISA
酷 壳 – CoolShell
酷 壳 – CoolShell
量子位
Engineering at Meta
Engineering at Meta
爱范儿
爱范儿
aimingoo的专栏
aimingoo的专栏
S
Security Affairs
P
Privacy & Cybersecurity Law Blog
B
Blog RSS Feed
AWS News Blog
AWS News Blog
P
Proofpoint News Feed
雷峰网
雷峰网
T
Tenable Blog
Schneier on Security
Schneier on Security
H
Heimdal Security Blog
V2EX - 技术
V2EX - 技术
V
V2EX
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
S
Secure Thoughts
Latest news
Latest news
Help Net Security
Help Net Security
Jina AI
Jina AI
Stack Overflow Blog
Stack Overflow Blog
The Cloudflare Blog
V
Vulnerabilities – Threatpost
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org

涛叔

自用 Claude Code 拼车中转服务 欢迎使用 @ZZ.AC 邮箱服务 我在用的 VPS 云主机 实现备份3-2-1原则 玩转 ZZ.AC 域名 ZZ.NIC 公益 Linux 主机空间 使用 Nginx 构建计费 DoH 代理服务 通过 Let’s Encrypt 为 IP 地址签发免费 SSL 证书
Go语言接口的空指针问题
2026-01-24 · via 涛叔

最近在调试某 Go 语言源项目的时候,发现某个变量值为 nil,但是 if v != nil 检测居然不通过😮这还是头一回遇到。一番折腾后发现接口变量的空指针跟普通的空指针还是有区别的。本文梳理相关研究内容作个备忘,也分享给有需要的读者。

先给出一段代码:

package main

import "fmt"

func main() {
  var j *int = nil
  var k any = j
  
  fmt.Printf("%v,%v\n", k, k == nil)
}

请问程序执行之后输出什么结果🧐

答案是<nil>,false

慢着,不是变量k的值已经输出是<nil>了,怎么后面的k == nil结果还是false呢?这不是自相矛盾吗?

我们回到代码中。这里的变量k类型为any,即interface{},可以保存任意变量。而变量j为整型指针,它的值为nil

从直觉上看,因为j的值是nil,所以我们自然希望把j赋给k后,k的值也是nil。而前面输了结果中的第一部分为<nil>,在某种程序上「符合」我们的直觉。所以后面的 false才会更显诡异。

回到接口(interface)的本质,接口变量同时持有保存对象的地址和类型。要想让k == nil 结果为true,就需要该接口变量保存的地址和类型同时为空才可以!在前例中,虽然变量 j的值已经是nil了,但赋值给变量k之后,k同时还保存了*int类型信息,所以它不是nil

但为什么前面的代码中还会输出<nil>呢,那是因为fmt.Printf()会自动使用反射提取接口变量指向的变量,再输出目标变量的值,结果自然是nil了。

为此,我们可以运行以下代码来验证:

package main

import "fmt"

func main() {
  var k any = nil
  
  fmt.Printf("%v,%v\n", k, k == nil)
}

不出所料,结果是<nil>,true

一句话总结,对于 interface 变量,只要不是直接将 nil 赋值给他,任何其他赋值后该接口变量的值都不是nil!

好像也没有很麻烦,这有什么用呢?

这里是另外一段示例代码:

package main

import "fmt"

type Animal interface {
  speak()
}

type Dog struct{}

func (d Dog) speak() {
  fmt.Println("Woof!")
}

func print(a Animal) {
  if a != nil {
    a.speak()
  } else {
    fmt.Println("Dog is not present")
  }
}

func main() {
  var a *Dog = nil

  print(a)
}

这是一个典型的接口范例。代码先定义Animal接口,要求实现speak()方法。然后又定义了Dog类型,实现了Animal接口。接着又在print(a Animal)函数中接收动物接口实现,再根据变量值判断是否需要调用speak()方法。

函数print(a Animal)显然希望跳过空指针场景。但实际运行就会报如下错误:

panic: value method main.Dog.speak called using nil *Dog pointer

显然if a != nil {检查没有成功,也就是说变量a的值不为空,它的值是在调用print 函数传参时隐性赋予的,值为空的且类型为*Dog的指针。根据前文的分析,有类型的空指针不是空指针🤦‍♂️所以报错。

那要怎么做才能避免出现这类问题呢?

一种方法是避免使用指针,也就不存在传nil的情况,直接传值变量,简单粗暴!

另一个办法是利用反射,动态检查接口变量底层值是否为指针:

if h == nil || reflect.ValueOf(h).IsNil() {
  // ...
}

但使用反射可能存存性能问题,大家要注意。