通过memcpy或std::bit_cast创建的union活跃成员及相关未定义行为问题
问题解答
先给出代码示例:
#include <stdint.h> #include <string.h> #include <bit> union U { uint64_t ull; int32_t i[2]; }; int main(int argc, char *argv[]) { uint64_t x = argc; U u; if (true) u = std::bit_cast<U>(x); else ::memcpy(&u, &x, sizeof(x)); auto r = u.i[0]; r += u.i[1]; r += u.ull; return r; }
1. auto r = ...行执行前,union u的活跃成员是哪个?
当if(true)分支执行时,std::bit_cast<U>(x)会创建一个存储内容与x完全一致的U对象。根据C++标准,通过std::bit_cast构造的union对象,活跃成员为声明顺序的第一个非静态数据成员ull——因为bit_cast仅按位完整复制内存,未显式初始化union的特定成员,此时默认以第一个成员作为活跃成员。
2. 代码中哪些行存在未定义行为,原因是什么?
auto r = u.i[0];和r += u.i[1];存在未定义行为:
当前union的活跃成员是ull,而i属于非活跃成员。C++标准仅允许在特定场景下读取非活跃成员(如读取unsigned char类型、或成员为标准布局且共享公共初始序列),但uint64_t与int32_t[2]既不属于unsigned char类型,也无符合要求的公共初始序列,因此读取i的行为属于未定义行为。r += u.ull;无未定义行为,因为读取的是当前活跃成员ull。std::bit_cast的使用合法:U和uint64_t大小相同、均为标准布局类型,且对齐要求匹配,符合bit_cast的使用条件。
3. 将true改为false是否会改变上述问题的答案,若会,如何改变及原因是什么?
会改变部分答案:
- 活跃成员变化:进入
else分支后,u先被默认初始化(此时活跃成员为第一个成员ull,但未初始化),随后memcpy将x的内容复制到u的内存地址。memcpy仅修改内存字节,不会改变union的活跃成员,因此此时u的活跃成员依然是ull。 - 未定义行为的变化:读取
u.i[0]和u.i[1]依然是未定义行为,原因与之前一致——读取非活跃成员且不符合标准允许的例外场景。memcpy操作本身合法,它只是修改了活跃成员ull的内存内容,后续读取u.ull依然合法。
内容的提问来源于stack exchange,提问作者user541686
相关产品推荐
相关产品推荐

