应统一用 ptr == nullptr 判空,避免 NULL 或 0;非必要不冗余判空,优先用 std::optional/std::unique_ptr 等语义更明确的替代方案。

用 == nullptr 判断最直接,但别在所有地方都加
C++11 起,nullptr 是类型安全的空指针字面量,判断是否为空应统一用 ptr == nullptr(或 !ptr),**不要**用 ptr == NULL 或 ptr == 0 —— 后两者可能被误判为整数比较,尤其在函数重载或模板中容易引发歧义。
但并非每个指针访问前都要显式判空。比如:
- 类成员指针若由构造函数保证非空(如 RAII 封装的
std::unique_ptr),再判空是冗余 - 函数参数若文档明确要求“非空”,且调用方已约定契约(如
void process(int* p)注释写明p != nullptr),强行加判空反而掩盖设计问题 - 容器迭代器、
std::optional等替代方案本身已内建状态检查,不应再用裸指针模拟
解引用前不检查 nullptr 导致未定义行为
一旦解引用空指针(如 *p、p->foo()、p[0]),行为完全未定义:程序可能崩溃、静默返回垃圾值、甚至看似正常运行——这正是最难排查的坑。
常见触发场景:
立即学习“C++免费学习笔记(深入)”;
- 函数返回裸指针但未说明可空(如 C 风格 API 的
malloc失败返回nullptr) - 多线程中指针被另一线程释放后本线程未同步感知
- 智能指针(如
std::shared_ptr)已析构,但裸指针副本仍存在且未置nullptr
建议:对所有外部输入、系统调用返回、跨模块传递的裸指针,在首次解引用前做 if (p == nullptr) { /* handle */ }。
nullptr 和 std::optional / std::unique_ptr 的取舍
裸指针 + nullptr 检查只是最低成本的空值表示,但无法表达所有权和生命周期语义。
优先考虑替代方案:
- 表达“可选拥有权” → 用
std::unique_ptr:移动后自动变空,且if (ptr)语义清晰 - 表达“可选值”(不含资源管理)→ 用
std::optional:对非指针类型更自然,避免间接访问开销 - 需要共享所有权 →
std::shared_ptr,但注意循环引用风险 - 仅作观察/不控制生命周期 → 引用(
T&)比T*更能体现“必不为空”的契约
混用裸指针和智能指针时,切忌从 std::shared_ptr.get() 拿到裸指针后长期持有——它不延长生命周期。
调试阶段用断言快速暴露空指针误用
开发时可在关键解引用点插入 assert(p != nullptr),而非只靠运行时 if 分支。这样既避免发布版性能损耗,又能在测试中尽早失败。
几个实用习惯:
- 在类构造函数中对非空成员指针做
assert(ptr != nullptr) - 对私有辅助函数的指针参数加
assert,公有接口则用显式if报错或抛异常 - 禁用
assert的构建(如 Release)下,确保仍有基础nullptr检查兜底,不要完全依赖断言
真正难防的不是明面上的 nullptr,而是悬垂指针、竞态释放、或把未初始化的栈上指针当空指针处理——这些靠 == nullptr 检查根本无效。











