为何将union成员指针转为union指针并非UB?二者大小不同
C++联合体与指针强制转换的内存合法性问题
问题代码
class A { public: int x; A() { x = 5; } }; class B { public: int x; float y; B() { x = 10; y = 3.14; } }; union U { A a; B b; }; U* foo(U* u) { B* b = new B(); u->b = *b; return u; } int main() { A* a = new A(); U* u = (U*) a; u = foo(u); return u->b.y; }
核心疑问
这段代码执行u->b = *b;时会出现缓冲区溢出。查阅资料得知(U*)a这类转换应为良定义,但因sizeof(U) != sizeof(A)对此存疑;同时不确定U与A是否为指针可互转(pointer-interconvertible)类型,以及这是否意味着转换合法,难以理解相关C++标准。
问题解答
指针转换的合法性判定
C++标准中,只有两个类型属于指针可互转类型时,这类强制转换(C风格转换等价于reinterpret_cast)才是良定义的。指针可互转的场景包括:- 同一类型的对象指针
- 标准布局类型的非静态数据成员指针与所属对象指针
- 标准布局联合体的成员指针与所属联合体对象指针
但这里的情况完全不符合:
A对象是单独new出来的,并非联合体U的成员,所以A*转U*不属于合法的指针互转场景,本质是未定义行为。sizeof不匹配直接导致溢出
联合体的大小由其最大成员决定,sizeof(U)等于sizeof(B)(假设int为4字节、float为4字节,总大小8字节),而sizeof(A)仅为4字节。将仅分配了4字节内存的A对象强制转为U*后,访问并写入U::b(需要8字节内存)必然会越界,这就是缓冲区溢出的直接原因。对“转换良定义”的误区
部分资料提到的合法转换,指的是当联合体对象的活跃成员是某一成员时,该成员指针与联合体指针可安全互转(比如拥有一个U对象且活跃成员是a,此时U*和A*可互转)。但单独的A对象并非U的成员,完全不适用这个规则。
内容的提问来源于stack exchange,提问作者vwvw
相关产品推荐
相关产品推荐

