我具有以下构造,用于获取包含四个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这里最初是用于存储数据的双精度型。
我对位字段的假设是
这些正确吗?
测试使用
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
尽管这些值应该适合unsigned int's,但在所使用的类型之间切换有时会更改这些值。因此,感觉到它与添加到位域中的数据的解释方式有关。
通过添加#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位的情况下,它似乎不起作用。
所以,
长话短说-无论您在做什么,通过union
投射数据都是不确定的行为。因此,它可以正常工作,而并非偶然。仅允许您使用union
进行的操作读取您上次写入的成员。您执行其他任何操作,并且程序无效。
编辑:
即使允许,即使没有#pragma pack
,您也要依赖于结构中的数据对齐。大概是32位或64位。因此,在这种情况下,您的结构在内存中看起来确实像这样:
struct {
unsigned int a : 12;
unsigned int a_align: 20;
unsigned int b : 12;
unsigned int b_align: 20;
unsigned int c : 12;
unsigned int c_align: 20;
unsigned int d : 12;
unsigned int d_align: 20;
unsigned int unused : 16;
unsigned int unused_align: 16;
};
如果要从结构中提取一些数据,则可能应使用如下所示的掩码和位移位:
unsigned mask12 = 0xFFF;
unsigned a = data & mask12;
unsigned b = (data >> 12) & mask12;
unsigned c = (data >> 24) & mask12;
unsigned d = (data >> 36) & mask12;
我为什么看到这个问题开始?
正确地,访问联盟的不活动成员具有未定义的行为。但是让我们假设您的系统允许它。
unsigned
可能是32位。 a和b装入第一个unsigned
,总共占用24位。此unsigned
仅剩8位。 12位c不适合该8位插槽。因此,它改为开始一个新的unsigned
并保留8位填充。
这是一个可能的结果。位域布局由实现定义。在另一个系统上,您可能会看到预期的结果。
#pragma pack语句可以解决此问题吗?
它可能会更改布局规则,以允许跨多个基础对象“跨越”位域。