37、空指针:NULL与nullptr的区别及解引用风险

空指针,这玩意儿是C语言里最经典的坑之一。我入行那会儿,第一次因为解引用空指针导致程序崩溃,debug了一整个下午。后来发现,其实就是少了个判空。嗯,从那以后,我对空指针就格外敏感。

今天咱们聊聊两个东西:NULLnullptr。很多人觉得它们差不多,其实差别不小。再加上解引用空指针的风险,这几乎是每个嵌入式工程师都会遇到的噩梦。

NULL 是什么?

NULL 在C语言里,其实就是一个宏定义。通常是这样:

#define NULL ((void*)0)

说白了,它就是 0 的一个别名。只不过编译器把它当作指针来看待。但这里有个问题——NULL 的类型是 void*,不是具体的指针类型。这在C++里会引发一些麻烦。

举个例子:

void func(int* p);
void func(int n);

func(NULL);  // 到底调用哪个?

在C++里,NULL 会被当作整数 0 来处理,于是编译器会优先调用 func(int)。这显然不是我们想要的。我在项目中遇到过这种重载歧义,排查起来特别头疼。

nullptr 的诞生

nullptr 是C++11引入的关键字。它的类型是 std::nullptr_t,可以隐式转换为任何指针类型,但不会转换为整数。这就解决了上面的歧义问题。

func(nullptr);  // 明确调用 func(int*)

我个人习惯在C++项目里一律用 nullptr,不再碰 NULL。你想想看,既然语言提供了更安全的工具,为什么不用呢?

C语言里怎么办?

如果你还在写纯C代码,那只能用 NULL。不过别担心,C语言里没有函数重载,所以 NULL 的歧义问题基本不存在。但要注意一点:NULL0 在C里是等价的,所以有些编译器可能会给出警告。

我建议在C代码里,指针初始化统一用 NULL,不要用 0。这样代码可读性更好,别人一看就知道这是个指针。

解引用空指针的风险

这才是重点。解引用空指针,说白了就是访问地址为 0 的内存。操作系统通常不允许这么做,于是程序就会崩溃——段错误(Segmentation Fault)。

为什么会这样?因为地址 0 被操作系统保留,用来捕获空指针错误。你一旦访问,内核就会发信号干掉你的进程。

在嵌入式环境里,情况更复杂。有些单片机没有MMU,地址 0 可能是合法的内存区域。这时候解引用空指针不会崩溃,但会读写到不该碰的地方,导致各种诡异的问题。我曾经在一个ARM Cortex-M3的项目里遇到过,空指针赋值后系统没死,但某个全局变量莫名其妙变了。查了两天才发现是空指针的锅。

警告: 永远不要假设解引用空指针一定会崩溃。在裸机环境下,它可能静默地破坏数据,让你死都不知道怎么死的。

如何避免空指针解引用?

方法其实很简单,就两条:

  1. 初始化指针时赋空int* p = NULL;int* p = nullptr;
  2. 使用前判空if (p != NULL) { *p = 42; }

听起来像废话对吧?但我在代码审查里,几乎每次都能看到有人忘了判空。尤其是函数参数,传进来的指针是不是空,你根本不知道。

void set_value(int* p, int val) {
    if (p == NULL) {
        return;  // 或者打印错误日志
    }
    *p = val;
}

嗯,这里要注意:判空之后,要么返回错误码,要么直接 return。千万别继续往下走。

NULL 与 nullptr 的对比

特性 NULL nullptr
语言 C / C++ C++11 及以上
本质 宏,通常为 ((void*)0) 或 0 关键字,类型为 std::nullptr_t
类型安全 差,可被当作整数 好,只能转为指针
重载场景 可能引发歧义 无歧义
推荐用法 C 代码 C++ 代码

知识结构图

下面这张图帮你理清空指针的核心知识点:

空指针 NULL(宏) 类型:void* 或 0 C 语言推荐使用 nullptr(关键字) 类型:std::nullptr_t C++ 推荐使用 解引用空指针 → 段错误 / 数据损坏
小技巧: 在嵌入式开发中,我习惯在调试阶段把空指针解引用做成断言(assert),这样一触发就能立刻定位问题。发布版本再关掉断言,换成安全返回。

避坑指南

我曾经在一个通信协议栈里,因为没判空,导致野指针覆盖了关键配置数据。设备运行了三天才出问题,排查起来简直要命。从那以后,我给自己定了几条铁律:

  • 所有指针变量定义时,必须初始化(NULL 或 nullptr)。
  • 所有函数参数如果是指针,必须判空(除非文档明确说不会传空)。
  • 释放内存后,立即把指针置空:free(p); p = NULL;
  • 代码审查时,空指针相关逻辑必须逐行过。

说白了,空指针问题不是技术难题,而是习惯问题。你只要养成每次用指针前都问自己一句“它会不会是空?”的习惯,就能避开90%的坑。

嗯,今天就聊到这儿。记住:NULL 和 nullptr 的区别要搞清楚,但更重要的是——别解引用空指针。


专注资料整理