必须用 nullptr 而不是 null,因为 null 是整数 0 或宏定义,重载解析时可能误匹配 int 参数;nullptr 是类型安全的 std::nullptr_t 字面量,只隐式转换为指针类型,保障重载、模板推导和类型系统正确性。

为什么必须用 nullptr 而不是 NULL
因为 NULL 在 C++ 中本质是整数 0 或宏定义的 0L,它在重载解析时可能意外匹配到 int 参数而非指针参数。而 nullptr 是类型安全的空指针字面量,其类型是 std::nullptr_t,只能隐式转换为任意指针类型,不会转成整数。
常见错误现象:void foo(int) { } void foo(char*) { } foo(NULL); —— 这会调用 foo(int),不是预期行为;但 foo(nullptr) 明确调用 foo(char*)。
-
NULL可能是0、0L或__null,取决于头文件和编译器实现 -
nullptr是 C++11 引入的关键字,不依赖宏,类型固定且唯一 - 模板推导中
NULL会导致T = int,而nullptr推导出T = std::nullptr_t(或适配为指针类型)
nullptr 的典型使用场景
主要用在需要明确表达“空指针”语义、且类型安全至关重要的地方:函数参数、返回值、初始化、模板特化判断等。
例如:
立即学习“C++免费学习笔记(深入)”;
void process(const char* s) { /* ... */ }
process(nullptr); // 安全,明确指向指针重载
<p>template<typename T>
void handle(T<em> ptr) {
if (ptr == nullptr) { /</em> ... */ } // 比 ptr == NULL 更清晰、更类型安全
}</p><p>std::unique_ptr<int> p = nullptr; // 合法,等价于 std::unique_ptr<int> p;- 类成员指针初始化:
int (MyClass::*ptr)() = nullptr;——NULL在此上下文中可能不被接受(尤其旧编译器) - 智能指针赋值:
std::shared_ptr<t> sp = nullptr;</t>是标准写法,= NULL可能触发隐式转换警告 - 返回空指针的工厂函数:
Base* create(bool valid) { return valid ? new Derived : nullptr; }
nullptr 和 0、NULL 的兼容性与编译器行为
C++11 起,nullptr 是保留关键字,不能用作标识符;而 NULL 是宏,仍可被 undef 或重定义(不推荐)。所有主流编译器(GCC、Clang、MSVC)在 C++11 及以上模式下都支持 nullptr,且默认开启。
- 如果代码需兼容 C++03,不能用
nullptr,但应避免NULL用于指针上下文,改用0(虽不理想,但至少是整数字面量) - 启用
-Wzero-as-null-pointer-constant(Clang/GCC)可警告用0或NULL代替nullptr的地方 - MSVC 在 /Za(禁用扩展)下可能拒绝
NULL用于模板非类型参数,但接受nullptr
面试常踩的坑:别混淆 nullptr 的类型和用途
很多人知道 nullptr 是空指针,但忽略它本身不是指针类型,而是 std::nullptr_t——一个可隐式转换为任意指针类型的独立类型。
-
sizeof(nullptr)不一定等于sizeof(void*)(尽管通常相同),它只是设计为能容纳所有指针值 -
nullptr不能用于算术运算:nullptr + 1是非法的;NULL + 1可能意外通过(变成整数加法) - 不能取
nullptr的地址:&nullptr编译失败;而&NULL可能因宏展开为&0导致错误或未定义行为 - 在 SFINAE 或
constexpr上下文中,nullptr是字面量常量,NULL不是(宏展开后才确定)
最易被忽略的一点:nullptr 解决的从来不是“有没有空值”的问题,而是“如何让空值在类型系统里不污染重载和模板推导”。一旦写了 NULL,你就已经把类型选择权交给了预处理器。











