&& 和 || 能防止空指针访问,因其短路特性:左侧结果确定时跳过右侧求值,如 ptr && ptr->member 中 ptr 为空则不执行解引用;但重载逻辑运算符会破坏该机制。

为什么 && 和 || 能防止空指针访问
因为 C++ 标准规定:逻辑运算符 && 和 || 从左到右求值,且一旦结果已确定就**立即停止后续表达式计算**(即“短路”)。这意味着如果左侧操作数已经足以决定整个表达式的真假,右侧就不会执行——哪怕它包含对空指针的解引用。
典型写法:ptr && ptr->member
这是最常用、也最安全的模式。只要 ptr 为 nullptr,ptr->member 根本不会被求值,自然不会触发段错误。
常见误用场景:
- 写成
ptr->member && some_condition:一旦ptr为空,直接崩溃,短路没机会生效 - 在函数调用中传入
ptr->member作为参数,而没提前检查:函数参数求值发生在调用前,短路无法保护 - 用逗号运算符或赋值表达式替代逻辑运算符,比如
(ptr, ptr->member)或(p = ptr) && p->member—— 后者可行,但前者完全不短路
|| 在空指针场景下的正确用法
|| 短路适用于“兜底”逻辑,例如提供默认值或 fallback 行为:
int value = (ptr && ptr->valid()) ? ptr->data : 0;
// 更简洁等价写法(需确保 data 是可转换为 int 的类型):
int value = ptr && ptr->valid() ? ptr->data : 0;
// 或用 || 实现“非空则用,否则用默认对象”:
const auto& obj = (ptr ? *ptr : default_obj);
// 注意:这里不是 || 的短路,而是三目运算符;真正的 || 示例:
if (!ptr || !ptr->ready()) {
// ptr 为空,或非空但未就绪 → 统一处理
}
关键点:
-
||左侧为真时,右侧不执行;所以!ptr || !ptr->ready()是安全的(左侧!ptr为真即跳过右侧) - 但
ptr->ready() || other_condition是危险的——必须先确保ptr非空 - 不要依赖
||来“避免解引用”,它的安全前提仍是左侧检查覆盖了空指针情况
容易被忽略的坑:重载运算符会破坏短路
如果 ptr 是智能指针(如 std::shared_ptr)或自定义指针类,且重载了 operator&& 或 operator||,那么原生短路语义将失效——此时是函数调用,左右操作数都会被求值。
例如:
std::shared_ptr<foo> sp;
if (sp && sp->bar()) { ... } // ✅ 安全:shared_ptr 的 operator bool() + 内置 &&</foo>
但若有人写了:
struct BadPtr {
bool operator&&(const BadPtr&) { return true; }
};
BadPtr p;
if (p && p->crash()) { ... } // ❌ p->crash() 一定会执行!
所以务必确认你用的“指针类型”没有重载逻辑运算符;对标准智能指针放心,对第三方或自研封装要查源码或文档。
真正保险的做法永远是显式判空,短路只是语法糖,不是银弹。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











