17370845950

C++ nullptr和NULL区别 C++空指针安全使用建议【规范】
C++中NULL本质是整数0,非指针类型,易导致函数重载错误;应使用C++11引入的类型安全关键字nullptr,它为std::nullptr_t类型,可隐式转换为任意指针但不转整数,支持正确重载、模板推导和显式空指针比较。

NULL 在 C++ 中本质是整数 0,不是指针类型

很多老代码里写 int* p = NULL;,看着没问题,但编译器其实把它当成了 int* p = 0;。这在函数重载场景下会出问题:void foo(int)void foo(char*) 同时存在时,foo(NULL) 会意外调用 foo(int),而不是你期望的指针版本。

原因在于:C++ 标准规定 NULL 是实现定义的宏,常见实现是 #define NULL 0#define NULL 0L,始终是整型常量。

  • 不推荐在 C++11 及以后使用 NULL 表示空指针
  • 若需兼容旧代码,确保头文件中未自行重定义 NULL
  • NULL 在 C 头文件(如

    )中定义,C++ 中应优先用标准空指针字面量

nullptr 是类型安全的空指针字面量

nullptr 是 C++11 引入的关键字,类型为 std::nullptr_t,能隐式转换为任意指针类型,但不会转成整数类型。这意味着它能正确参与函数重载、模板推导和 auto 类型推导。

例如:auto p = nullptr; 推导出 std::nullptr_t,而 auto q = NULL; 推导出 int —— 这直接影响后续赋值和比较行为。

  • 所有新代码必须用 nullptr 替代 NULL 初始化或比较指针
  • 不能对 nullptr 执行算术运算(如 nullptr + 1),编译直接报错,比运行时崩溃更早暴露问题
  • sizeoftypeid 等操作符配合良好,语义清晰

判断空指针时避免隐式转换陷阱

if (p == nullptr) 是安全的;但写 if (p == 0)if (!p) 虽然通常可行,却隐藏了类型风险。尤其在模板或泛型代码中,!p 可能触发用户自定义的 operator!,而非指针判空逻辑。

更隐蔽的问题出现在容器查找中:比如 std::map m;,用 m.find(0) 会因类型不匹配导致编译失败(0int,不是 int*),而 m.find(nullptr) 可行。

  • 统一用 p == nullptrp != nullptr 做显式空指针比较
  • 禁用 if (p) / if (!p) 风格(除非团队明确约定且静态检查覆盖)
  • 在模板参数、容器键类型、SFINAE 场景下,nullptr 是唯一可安全传递的空指针字面量

跨平台和遗留系统中的实际妥协点

极少数嵌入式编译器(如某些 ARM GCC 4.7 旧版本)或严格 C++03 环境不支持 nullptr。此时不应退回到 NULL,而应采用兼容性封装:

#ifdef __cplusplus11
    #define SAFE_NULLPTR nullptr
#else
    #define SAFE_NULLPTR 0
#endif

但注意:这种宏仅用于初始化(int* p = SAFE_NULLPTR;),不可用于比较或模板实参——后者在 C++03 下本就不支持泛型空指针语义。

  • 真正需要 C++03 兼容时,优先升级工具链,而非妥协语义
  • 禁止在头文件中无条件 #define NULL nullptr,会破坏 C 头文件包含行为
  • 第三方库若内部用 NULL,无需修改;但你的新代码与之交互时,仍应传 nullptr(只要接口接受指针类型)

最易被忽略的是:即使用了 nullptr,也不能替代对指针生命周期的管理。悬空指针、释放后使用、多线程竞态等问题,nullptr 一个都拦不住——它只解决“怎么写空”这个语法层问题,不解决“什么时候该为空”这个逻辑层问题。