我可以使用 union 在不同大小的整数之间进行转换吗?

问题描述

让我们考虑不同大小的整数的联合。是否保证如果一个数字符合每个整数类型的范围,它就可以正确地写入和读取任何联合数据成员?

例如这段代码

  union U {
    int32_t i;
    int16_t s;
  } u;
  u.s = 1000;
  std::cout<<u.i<<std::endl;

我确认它在一台计算机上正确打印了“1000”。是否保证在任何其他系统上都能正常工作?我猜在任何系统上,任何整数类型的字节序都是相同的,所以问题是联合是否保证将较大整数的较低有效字节用于较小的整数?

我知道这对负数不起作用,所以让我们只考虑非负数。

解决方法

不,这只适用于小端。在 big endian 中,字节从最重要的位置向下存储。 0xdeadbeef 将在内存中以小端和 ef be ad de 大端存储在内存中,因此从起始地址读取 2 个字节将分别导致这些机器中的 0xbeef 和 0xdead

但是,您得到了未定义的行为,因为您要先写入 较小 2 字节字段,然后再读取 较大 字段。这意味着 de ad be ef 的高 2 个字节将包含垃圾。这忽略了在 C++ 中使用 int32_t 已经是 UB 的事实。你只能在 C 中做到这一点

如果您先写入较大的字段读取较小的字段,那么它甚至适用于签名类型:

union

这将在小端机器上按预期打印

,

联合体的大小将始终是容纳其最大数据成员所需的大小。

其他数据成员分配在与最大成员相同的字节中。

“其他成员”如何分配的细节是实现定义的。根据这个定义,答案是否定的,不能保证。 请参阅:https://en.cppreference.com/w/cpp/language/union“说明”

从 C++14 开始,联合的所有非静态数据成员将具有相同的地址;但这并没有说明字节序或实现支持。