Skip to content

14.4 空指针 nullptr

在老版本的 C 和 C++98 代码汇总,你一定见过无数次用大写的 NULL 来赋给一个指针表示它是空的。

int* p = NULL;

但这其实是一个隐伏了数十年的设计漏洞。在 C++11 中,请彻底忘掉并且永远不要再写 NULL,使用新关键字 nullptr 取而代之。

因为 C++ 不像 C 那样允许 void* 随意隐式转换为任意别的类型的指针,所以在 C++ 源码早期的标准头文件中,NULL 被暴力无情地使用预处理器宏定义为了一个整数的零

#define NULL 0

也就是说,**你自以为的“空指针”,在编译器眼里只是一串冷冰冰的整数 0 字面常量。**这就引发了类型重载匹配时的超级灾难。

请看下例:

#include <iostream>
void printAction(int number) {
std::cout << "我收到一个整数。\n";
}
void printAction(int* ptr) {
std::cout << "我收到一个指针。\n";
}
int main() {
printAction(0); // 毫无疑问,调用的上面的整型重载版
printAction(NULL); // 糟糕!本意想传空指针的你调用的依然是上面的整型重载版!
}

原本你应该期望传 NULL 进去时匹配到指针那个重载签名,结果编译器死活认准了 NULL 被宏展开为了 0,于是跑去了第一个重载函数里去了。

C++11 引入的 nullptr 并不是一个宏,它是一个全新的关键字,其类型是 std::nullptr_t

你可以这样理解它:

  1. 只能被转化为任意类型的裸指针或者智能指针,或者与各种指针发生比较(ptr == nullptr)。
  2. 绝对不能隐式转换为任何整型类型的值

有了它,重载函数就可以被正确匹配了!

int main() {
printAction(0); // 匹配 printAction(int)
printAction(nullptr); // 完美匹配真正接受空指针参数的 printAction(int*)
}
  1. 如果是表示一个数字,写 0
  2. 如果是表示字符串的结束符,或者想赋值单个字符置空,写 '\0'
  3. 如果是代表没有指向任何内存的指针标志,使用 nullptr

永远不要将三者混用,永远告别 NULL