请注意,int 位域是有符号还是无符号是实现定义的。 C++ 标准是这么说的,而 C 标准用不同的措辞实现了相同的最终结果:
ISO/IEC 14882:2011 — C++
§7.1.6.2 简单类型说明符
¶3 ... [注意:char 类型的对象和某些位域(9.6)是否是实现定义的
表示为有符号或无符号数量。 signed 说明符强制 char 对象和位域为
签;它在其他情况下是多余的。 ——尾注]
§9.6 位域
¶3 ... 位域应具有整数或枚举类型 (3.9.1)。这是
实现定义是否是一个普通的(既没有显式签名也没有无符号)char,short,int,long,
或long long 位域有符号或无符号。
ISO/IEC 9899:2011 — C
§6.7.2.1 结构和联合说明符
¶10 位域被解释为具有由指定位数组成的有符号或无符号整数类型。125)
125) 如上面 6.7.2 所述,如果实际使用的类型说明符是 int 或定义为 int 的 typedef-name,则位域是否带符号由实现定义或未签名。
§6.7.2 类型说明符
¶5 ... 对于位域,说明符 int 是否指定与 signed int 相同的类型或与 unsigned int 相同的类型是实现定义的。
第 6.7.2 节的上下文表明 int 可以与 short、long 等组合,并且适用该规则; C++ 更清楚地说明了这一点。当然,普通char 的签名已经由实现定义了。
无符号位域
如果位域的类型是无符号的,那么表达式相当简单:
int d = (example.a << 7) | example.b;
有符号位域
如果这些值是有符号的,那么您需要进行主要的解释练习,确定如果example.a 为负数而example.b 为正数,则该值应为多少,反之亦然。在某种程度上,即使值都为负或均为正,也会出现问题。
假设example.a = 7; 和example.b = 12; — d 的值应该是多少?可能同样的表达式适用,但您可能会争辩说最好少移动 1 个位置:
assert(example.a >= 0 && example.b >= 0);
int d = (example.a << 6) | example.b; // Alternative interpretation
其他情况由您决定;这取决于您要对这些值进行的解释。例如:
int d = ((example.a & 0x0F) << 7) | (example.b & 0x7F);
这会强制将有符号值视为无符号值。这可能不是你想要的。
修改后的问题
example.a = 1001 // binary
example.b = 1010101 // binary
d = 10011010101 xxxxxxxxxxxxxxxxxxxxx
其中 x 可以是之前属于 d 的 21 位。
为此,您需要:
d = (d & 0x001FFFFF) | ((((example.a & 0x0F) << 7) | (example.b & 0x7F)) << 21);
您可能可以使用更少的括号;我不确定我是否会冒险这样做。
联合
但是,使用此修订后的规范,您可能很想查看union,例如:
union u
{
struct
{
int a:4;
int b:7;
int c:21;
} y;
int x;
} example;
但是,未指定位域中的位布局 w.r.t int x; 中的位(它们可能是最高有效位在前或最低有效位在前),并且总是有关于“如果你访问联合中的一个值,该值不是分配给您的最后一个值调用未定义的行为。因此,您需要处理位字段的多个平台定义方面。事实上,这种难题通常意味着位域与一种特定类型的机器 (CPU) 以及编译器和操作系统密切相关。就您所追求的细节而言,它们非常非常不便携。