union成员赋值后其他成员未定义,因所有成员共享同一内存地址,写入一个成员会覆盖其他成员的二进制位;C++标准规定仅可读取最后写入的活跃成员,否则为未定义行为(UB),编译器可生成任意结果。

union 成员赋值后其他成员为什么是未定义的
因为所有成员共享同一块内存起始地址,写入 i 时,4 字节被按整型解释;紧接着写入 f,这 4 字节被按浮点格式重写,i 对应的二进制位已不是合法整数值。C++ 标准明确将此时读取 i 定义为“未定义行为(UB)”,不是“值变乱了”,而是编译器可以生成任意结果(包括崩溃、优化掉该读取)。
- 只允许访问**最后被赋值的那个成员**,这是使用 union 的铁律
- 不能依赖
sizeof推断可安全读取的成员:比如union { int i; char c[4]; }中,即使c占满全部 4 字节,写完i后再读c仍是 UB(除非用std::memcpy或std::bit_cast等显式重解释) - C++17 起推荐用
std::variant替代手动管理活跃成员的 union,它自带类型标签和安全访问
怎么安全地把 float 拆成 4 个字节看
常见错误是直接用 union { float f; uint8_t b[4]; } 然后写 f = 3.14f 再读 b —— 这在 C++20 前属于未定义行为(类型双关 violation)。正确做法分两种:
- 用
std::bit_cast(C++20):auto bytes = std::bit_cast<:array>>(3.14f);</:array> - 用
std::memcpy(全标准兼容):uint8_t bytes[4]; std::memcpy(bytes, &f, sizeof(f)); - 若坚持用 union,需确保两个成员是“可平凡复制且可平凡比较”的类型,并启用编译器特定扩展(如 GCC/Clang 的
-fno-strict-aliasing),但不推荐用于生产代码
匿名 union 怎么简化访问但要注意什么
匿名 union 允许成员直接暴露在外层作用域,省去点号访问,例如:
struct Packet {
uint16_t header;
union {
struct { uint32_t ip; uint16_t port; } tcp;
struct { uint8_t mac[6]; } arp;
}; // 匿名,无名
};
这样可以直接写 p.tcp.ip 或 p.arp.mac[0]。但注意:
立即学习“C++免费学习笔记(深入)”;
- 匿名 union 不能有成员函数、静态成员或私有/保护成员
- 不能含引用类型或非平凡析构类型(如
std::string)—— 即使 C++11 允许带构造函数的类作 union 成员,匿名 union 也不行 - 必须嵌套在类或结构体中,不能全局定义匿名 union
union 和 struct 混用做协议解析时对齐怎么算
union 大小由其最大成员决定,但对齐要求取所有成员中**最大对齐数**。例如:
union U {
double d; // 对齐 8,大小 8
char c[10]; // 对齐 1,大小 10 → union 大小向上对齐到 16(因 max align=8)
};
当 union 嵌套在 struct 中,整个 struct 对齐受其最严格成员约束。容易踩的坑:
- 不要假设
sizeof(union { int i; char c[5]; }) == 5:实际通常是 8(因int对齐为 4,向上取整到 4 的倍数) - 用
alignas(1)强制取消对齐可能破坏硬件访问(如某些 ARM 平台要求 float 必须 4 字节对齐) - 网络协议解析时,建议用
#pragma pack(1)或__attribute__((packed))控制 struct,而非靠 union 调整对齐
真正难的不是写 union,而是始终记住:它不帮你记哪个成员当前有效,也没运行时类型检查——一不小心越界读,调试器都救不了你。


















