nullptr 是类型安全的空指针字面量,类型为 std::nullptr_t,可隐式转换为任意指针类型但不转换为整数;null 通常定义为 0,易引发函数重载歧义。

nullptr 是类型安全的空指针字面量,NULL 不是
在 C++11 及以后,nullptr 是专门引入的空指针常量,类型为 std::nullptr_t,能隐式转换为任意指针类型,但**不会隐式转换为整数类型**。而 NULL 通常是宏定义,常见实现是 0 或 ((void*)0) —— 在 C++ 中更大概率是 0(即 int),这会导致函数重载歧义。
比如有这两个重载:
void foo(int) { std::cout << "int\n"; }
void foo(char*) { std::cout << "ptr\n"; }
调用 foo(NULL) 会调用 foo(int)(因为 NULL 是 0),而 foo(nullptr) 明确匹配 foo(char*)。
NULL 的定义依赖头文件和编译器,行为不统一
NULL 不是语言关键字,而是标准库提供的宏,其定义位置和值可能因环境而异:
立即学习“C++免费学习笔记(深入)”;
- 在
<cstddef></cstddef>中,C++ 标准只要求它“可转换为指针类型”,未规定具体展开形式 - MSVC 常定义为
__null或0;GCC/Clang 在 C++ 模式下通常定义为0 - 某些老旧代码或嵌入式环境可能手动定义
#define NULL ((void*)0),但这在 C++ 中是非法的(void*不能隐式转为其他指针)
这意味着用 NULL 写的代码,在跨平台或升级编译器后可能突然编译失败或行为异常。
用 nullptr 能触发模板推导和 constexpr 上下文
nullptr 是字面量,属于核心常量表达式(constexpr),可用于模板非类型参数、static_assert、数组大小等场景;NULL 因为是宏,展开后可能是 0(整数),无法用于需要指针常量的地方。
例如:
template<typename T> void bar(T* ptr); <p>bar(nullptr); // OK:T 推导为 void,bar<void>(nullptr) bar(NULL); // 错误:NULL 是 int,无法匹配 T*
再如:
constexpr auto p = nullptr; // OK // constexpr auto q = NULL; // 不安全:取决于 NULL 展开结果
什么时候还能用 NULL?基本不用了
除非维护 C 风格遗留接口(比如调用 C 库函数并传入 NULL),否则没有理由继续用 NULL:
- C++11 起,所有标准库头文件(如
<iostream></iostream>)已确保nullptr可用 - 现代静态分析工具(如 clang-tidy)会警告
NULL的使用,建议替换为nullptr - 混合 C/C++ 项目中,若头文件被 C 和 C++ 同时包含,可保留
NULL仅用于 C 兼容部分,但 C++ 实现侧一律用nullptr
真正容易被忽略的是:即使写了 #include <cstddef></cstddef>,也不代表你就该用 NULL —— 它只是让 NULL 可用,而不是推荐它。











