我有以下构造,旨在获取包含四个 12 位值的 48 位值并提取它们。
struct foo {
union {
unsigned __int64 data;
struct {
unsigned int a : 12;
unsigned int b : 12;
unsigned int c : 12;
unsigned int d : 12;
unsigned int unused : 16;
};
};
} foo;
然后使用分配有问题的数据
foo.data = (unsigned __int64)Value;
Value 这里最初是一个用于存储数据的 double 值。
我在创建位字段时的假设是
- 数据保存变量应该足够大以保存整个 数据且未签名。因此,无符号 __int64 可以容纳 48 位。
- 每个位域成员的类型都应该足够大以容纳 分配给它的位数且无符号。
- 如果满足以下条件,最好使所有位域成员保持相同类型: 可能的。 (以避免对齐问题?)
- 实际上并不需要未使用成员。
这些正确吗?
测试
Value = 206225551364
我们得到一个应包含位的值
0000 0011 0000 0000 0100 0000 0000 0011 0000 0000 0100
这应该导致
a: 0000 0000 0100 = 4
b: 0000 0000 0011 = 3
c: 0000 0000 0100 = 4
d: 0000 0000 0011 = 3
但是运行这个实际返回的值是
a: 4
b: 3
c: 48
d: 0
虽然这些值应该适合无符号整数,但有时使用的类型切换会改变值。所以感觉它与添加到位字段时如何解释数据有关。
通过添加 #pragma pack(1)(我知道这与对齐有关但不经常遇到),我突然得到了预期值。
struct foo {
union {
unsigned __int64 data;
#pragma pack(1)
struct {
unsigned int a : 12;
unsigned int b : 12;
unsigned int c : 12;
unsigned int d : 12;
unsigned int unused : 16;
};
};
} foo;
a: 4
b: 3
c: 4
d: 3
但是接受这一点我感到不舒服。我想理解它,从而确保它实际上可以工作,而不仅仅是看起来可以工作,例如,当值不占用超过 4 位时。
所以,
- 我为什么会看到这个问题?
- #pragma pack 语句如何解决该问题?
- 能否推断出这何时会成为问题,何时不会?是不是因为 例如,这些值是 12 位而不是 8 位或 16 位?
最佳答案
why am I seeing the issue to begin with?
首先,访问 union 体的非事件成员具有未定义的行为。但让我们假设您的系统允许这样做。
unsigned
可能是 32 位。 a 和 b 适合第一个 unsigned
,总共 24 位。这个unsigned
只剩下8位了。 12 位 c 不适合这个 8 位插槽。因此,它会启动一个新的 unsigned
,留下 8 位填充。
这是一种可能的结果。位字段布局是实现定义的。在另一个系统上,您可能会看到您期望的结果。或者输出与您的预期不同,也与您在此处观察到的不同。
What does the #pragma pack statement do that fixes the issue?
它可能会更改布局规则,以允许位域“跨越”多个底层对象。这可能会使访问速度有点变慢。
Can one deduce when this will become a problem and not?
如果您不尝试跨过底层对象,那么布局是否支持这一点不会有任何差异。在这种情况下,您可以简单地使用 64 位底层对象。
这并不是位域布局可能与您期望的不同的唯一方式。例如,位域可以是最重要的第一个或最后一个。 unsigned
本身的位数是实现定义的。
一般来说,位集的布局不值得依赖。
How would, what I want to achieve best be done then?
为了避免 UB,您可以创建另一个对象,然后将字节复制到另一个对象上,而不是通过 union 进行双关。但首先,您必须确保对象具有相同的大小。可以使用 std::memcpy
或 std::bit_cast
完成复制。
为了避免跨接问题,请使用完全填充每个底层对象的位域集。在本例中,使用 64 位底层对象。
为了获得可靠的布局,首先不要使用位域。 bartop展示了如何使用轮类和蒙版来做到这一点。 (尽管,布局仍然依赖于字节顺序)
关于c++ - 与位域的 union 为位域成员提供了意想不到的值(value),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58975580/