C++是否保证指针转换为其他指针类型时表示具有一致性?
答复
核心结论
C++标准不保证任意指针类型转换后内部表示完全一致,不同转换场景的规则差异极大,我们结合你提供的测试代码逐一说明对应行为的标准约定:
#include <stdint.h> struct Foo {}; struct Bar : Foo {}; int main() { Bar obj; Foo * a = &obj; Bar * b = &obj; void *c = &obj; #if MAYBE_UB int * d = reinterpret_cast<int *>(&obj); #endif auto aa = reinterpret_cast<uintptr_t>(a); auto bb = reinterpret_cast<uintptr_t>(b); auto cc = reinterpret_cast<uintptr_t>(c); #if MAYBE_UB auto dd = reinterpret_cast<uintptr_t>(d); // UB? Not reading the pointee... #endif if (aa != bb) printf("bb differs\n"); if (aa != cc) printf("cc differs\n"); #if MAYBE_UB if (aa != dd) printf("dd differs\n"); #endif return 0; }
分场景规则说明
1. 派生类指针转非虚基类指针(对应Foo *a = &obj;)
- 你示例中的
Foo是Bar的非虚空基类,二者都属于标准布局类型:C++标准明确要求:标准布局类型的对象,其起始地址与所有非虚基类子对象的起始地址完全重合。该场景下
aa == bb是被标准强制保证成立的。 - 如果不符合标准布局要求(比如继承链存在虚函数、虚继承、基类和派生类都声明了非静态数据成员等),基类子对象在派生类的内存布局中会存在偏移,此时转换后的基类指针值会和原派生类指针值不同,
aa != bb属于合法实现。 - 空基类优化(EBCO)是当前所有主流编译器的默认行为,生产环境中几乎不会出现该场景下两个指针值不等的情况。
2. 对象指针转void*(对应void *c = &obj;)
- 标准明确规定:任意对象指针转换为
cv void*类型时,得到的指针指向该对象的起始存储地址。 - 因此该场景下
cc == bb是100%被标准保证成立的,不存在实现差异。
3. 用reinterpret_cast转不相关类型的对象指针(对应int *d = reinterpret_cast<int *>(&obj);)
- 首先明确:仅做指针类型转换、不解引用指向的内存的行为不属于未定义行为(UB),仅属于结果未指定。
- 标准对
reinterpret_cast的指针转换仅做了最低约束:将T1*转换为T2*再转换回T1*时,指针值保持不变。没有对中间转换得到的T2*的指针值做任何强制要求。 - 因此
dd == aa是否成立完全取决于编译器实现,标准没有任何约定。主流x86、ARM平台的编译器通常会保持指针值不变,但不能跨平台、跨编译器做通用假设。
测试程序行为总结
- 关闭
MAYBE_UB宏的情况下:标准布局场景下aa == bb成立,aa == cc始终成立,不会打印任何输出。 - 开启
MAYBE_UB宏的情况下:aa == dd没有标准保证,是否打印dd differs由编译器实现决定。
内容的提问来源于stack exchange,提问作者Thomas Eding
相关产品推荐
相关产品推荐

