不同对象的指针用、=比较是未定义行为;c++标准规定,仅当指针指向同一数组或同一结构体成员时比较才有定义,否则编译器可生成任意结果。

不同对象的指针用 或 <code>> 比较是未定义行为
只要两个指针指向**不同对象**(非同一数组、非同一结构体成员),用 、<code>>、、<code>>= 比较地址值,C++ 标准明确定义为未定义行为(UB)。编译器可以生成任意结果,包括崩溃、优化掉判断、或看似“正常”但不可靠的输出。
常见错误现象:
- 在
std::map或自定义排序中,把来自不同new的对象指针直接作为 key 比较 - 写一个全局容器,存一堆
Widget*,然后用std::sort(v.begin(), v.end())排序——若没提供自定义比较器,就隐式调用了 - 跨
std::vector::data()的指针做大小判断,误以为内存连续
只有 == 和 != 是安全的通用比较方式
== 和 != 是唯一被标准保证可在任意两个同类型指针间安全使用的比较运算符,无论它们是否指向同一对象、是否为空、是否已释放(只要不实际解引用)。
使用场景:
- 判断两个指针是否指向同一个对象(例如缓存命中、观察者去重)
- 检查指针是否为
nullptr或0 - 在多重继承中判断基类子对象与派生类对象的同一性(编译器会自动调整偏移)
注意:一旦转成 void* 再比较,比如 (void*)p1 == (void*)p2,虽然语法合法,但会丢失类型信息,导致原本应为 true 的基类/派生类指针比较变成 false —— 因为编译器不再做偏移修正。
数组内元素或结构体内成员的地址比较才真正有意义
只有当两个指针指向**同一数组的元素**,或**同一对象的非静态数据成员**,且这些成员在内存中实际连续(可通过 offsetof 验证),用 等比较才有可移植的语义。
实操建议:
- 遍历数组时用
ptr 是安全的,因为所有元素属于同一块连续内存 - 对结构体成员取地址后比较,先确认它们声明顺序和访问级别一致(同为
public且相邻),否则编译器可能重排 - 避免依赖
&obj.a 的结果,除非你查过 <code>offsetof(MyClass, a)确实小于offsetof(MyClass, b)
想排序多个对象?别比地址,比内容或引入稳定 ID
地址本身没有业务意义,且堆分配地址受 ASLR、分配器策略影响,每次运行都可能不同。真要排序,应该:
- 重载
operator,按业务字段(如 <code>id、name、时间戳)比较 - 给对象加一个
const uint64_t stable_id成员,在构造时用原子计数器赋值 - 用
std::less<t>{}(p1, p2)</t>—— 这是标准库提供的“总序”包装器,内部通过转换为uintptr_t实现,但仅用于容器排序等有限场景,不反映真实内存关系
最常被忽略的一点:哪怕你在调试时看到两个 new 出来的指针地址一前一后,也不能推断它们的生命周期或逻辑关系——下一次运行、换一个编译器、甚至加一行日志,顺序都可能变。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











