请注意,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++ 更清楚地说明了这一点。当然,plain 的签名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;
但是,未指定位字段中位的布局int x;
(它们可能是最高有效位在前或最低有效位在前),并且总是有关于“如果您访问联合中的值”的说法这不是分配给您的最后一个调用未定义的行为'。因此,您需要处理位字段的多个平台定义方面。事实上,这种难题通常意味着位域与一种特定类型的机器 (CPU) 以及编译器和操作系统密切相关。在您所追求的细节级别上,它们非常非常不便携。