在我问了这个问题大约2年多之后,我想用我想要的方式来解释它,当我还是一个完全的新手时,这对那些想了解这个过程的人来说是最有益的.
首先,忘记"11111111"示例值,它并不完全适合流程的可视化解释.因此,假设初始值为10111011
(187十进制),这将更好地说明该过程.
1 - how to read a 3 bit value starting from the second bit:个
___ <- those 3 bits
10111011
该值是101或小数形式的5,有两种可能的方法来获取它:
在这种方法中,首先用值00001110
(14位小数)屏蔽所需位,然后移位到位:
___
10111011 AND
00001110 =
00001010 >> 1 =
___
00000101
其表达式为:(value & 14) >> 1
此方法类似,但操作顺序颠倒,这意味着原始值被移位,然后用00000111
(7)进行掩码,仅保留最后3位:
___
10111011 >> 1
___
01011101 AND
00000111
00000101
其表达式为:(value >> 1) & 7
这两种方法都涉及相同的复杂性,因此在性能上不会有所不同.
2 - how to write a 3 bit value starting from the second bit:个
在这种情况下,初始值是已知的,当代码中出现这种情况时,您可能会想出一种方法,将已知值设置为另一个使用较少操作的已知值,但实际上很少会出现这种情况,大多数情况下,代码既不知道初始值,也不知道要写入的值.
这意味着为了将新值成功地"拼接"成字节,必须将目标位设置为0,之后将移位的值"拼接"到位,这是第一步:
___
10111011 AND
11110001 (241) =
10110001 (masked original value)
第二步是将我们想要写入3位的值移位,假设我们想要将其从101(5)更改为110(6)
___
00000110 << 1 =
___
00001100 (shifted "splice" value)
第三步(也是最后一步)是将屏蔽的原始值与移位的"拼接"值进行拼接:
10110001 OR
00001100 =
___
10111101
整个过程的表达式是:(value & 241) | (6 << 1)
Bonus - how to generate the read and write masks:
当然,使用二进制到十进制转换器远非完美,尤其是在32位和64位容器的情况下-十进制值变得非常大.可以使用表达式轻松地生成掩码,编译器可以在编译期间有效地解析这些掩码:
((1 << fieldLength) - 1) << (fieldIndex - 1)
, assuming that the index at the first bit is 1 (not zero)(1 << fieldLength) - 1
(index does not play a role here since it is always shifted to the first bit~
operator它是如何工作的(3位字段从上述示例中的第二位开始)?
00000001 << 3
00001000 - 1
00000111 << 1
00001110 ~ (read mask)
11110001 (write mask)
相同的示例适用于更宽的整数以及字段的任意位宽和位置,移位值和掩码值也相应地改变.
还要注意的是,示例假定无符号整数,这是您想要使用整数作为可移植位字段的替代方案(标准绝不保证常规位字段是可移植的),左移位和右移位都会插入一个填充0,而右移位有符号整数则不是这种情况.
Even easier:个
使用这组宏(但仅限于C++,因为它依赖于生成成员函数):
#define GETMASK(index, size) ((((size_t)1 << (size)) - 1) << (index))
#define READFROM(data, index, size) (((data) & GETMASK((index), (size))) >> (index))
#define WRITETO(data, index, size, value) ((data) = (((data) & (~GETMASK((index), (size)))) | (((value) << (index)) & (GETMASK((index), (size))))))
#define FIELD(data, name, index, size) \
inline decltype(data) name() const { return READFROM(data, index, size); } \
inline void set_##name(decltype(data) value) { WRITETO(data, index, size, value); }
你可以 Select 一些简单的东西,比如:
struct A {
uint bitData;
FIELD(bitData, one, 0, 1)
FIELD(bitData, two, 1, 2)
};
并将位字段实现为您可以轻松访问的属性:
A a;
a.set_two(3);
cout << a.two();
用gcc的typeof
pre-C++11替换decltype
.