如何在 C/C++ 中读/写任意位
假设我有一个二进制值为 11111111 的字节 b
Assuming I have a byte b with the binary value of 11111111
例如,如何读取从第二位开始的 3 位整数值或从第五位开始写入四位整数值?
How do I for example read a 3 bit integer value starting at the second bit or write a four bit integer value starting at the fifth bit?
推荐答案
在我提出这个问题大约 2 年多之后,我想以我想要的方式解释它,当我还是一个完整的新手时对想要了解流程的人最有益.
Some 2+ years after I asked this question I'd like to explain it the way I'd want it explained back when I was still a complete newb and would be most beneficial to people who want to understand the process.
首先,忘记11111111"示例值,这并不是真正适合对过程进行可视化解释的全部内容.所以让初始值是 10111011
(十进制的 187),这将更能说明这个过程.
First of all, forget the "11111111" example value, which is not really all that suited for the visual explanation of the process. So let the initial value be 10111011
(187 decimal) which will be a little more illustrative of the process.
1 - 如何从第二位开始读取 3 位值:
___ <- those 3 bits
10111011
该值为 101,即十进制的 5,有两种可能的获取方式:
The value is 101, or 5 in decimal, there are 2 possible ways to get it:
- 遮罩和移位
在这种方法中,需要的位首先用值 00001110
(十进制 14)进行掩码,然后将其移位:
In this approach, the needed bits are first masked with the value 00001110
(14 decimal) after which it is shifted in place:
___
10111011 AND
00001110 =
00001010 >> 1 =
___
00000101
此表达式为:(value & 14) >>1
- 移位和掩码
这个方法类似,但操作顺序相反,意思是将原始值移位,然后用00000111
屏蔽(7)只留下最后3位:
This approach is similar, but the order of operations is reversed, meaning the original value is shifted and then masked with 00000111
(7) to only leave the last 3 bits:
___
10111011 >> 1
___
01011101 AND
00000111
00000101
此表达式为:(value >> 1) &7
两种方法都涉及相同的复杂性,因此在性能上不会有所不同.
Both approaches involve the same amount of complexity, and therefore will not differ in performance.
2 - 如何从第二位开始写入 3 位值:
在这种情况下,初始值是已知的,当在代码中出现这种情况时,您可能会想出一种方法将已知值设置为另一个使用较少操作的已知值,但实际上这是很少有这种情况,大多数时候代码既不知道初始值,也不知道要写入的值.
In this case, the initial value is known, and when this is the case in code, you may be able to come up with a way to set the known value to another known value which uses less operations, but in reality this is rarely the case, most of the time the code will know neither the initial value, nor the one which is to be written.
这意味着为了成功拼接"新值到字节中,目标位必须设置为零,之后移位的值被拼接".到位,这是第一步:
This means that in order for the new value to be successfully "spliced" into byte, the target bits must be set to zero, after which the shifted value is "spliced" in place, which is the first step:
___
10111011 AND
11110001 (241) =
10110001 (masked original value)
第二步是将我们要写入的值移动到 3 位,假设我们要将其从 101 (5) 更改为 110 (6)
The second step is to shift the value we want to write in the 3 bits, say we want to change that from 101 (5) to 110 (6)
___
00000110 << 1 =
___
00001100 (shifted "splice" value)
第三步也是最后一步是将屏蔽的原始值与移位的拼接"拼接起来.价值:
The third and final step is to splice the masked original value with the shifted "splice" value:
10110001 OR
00001100 =
___
10111101
整个过程的表达式为:(value & 241) |(6 <<1)
奖励 - 如何生成读写掩码:
自然,使用二进制到十进制转换器远非优雅,尤其是在 32 位和 64 位容器的情况下 - 十进制值变得非常大.可以使用表达式轻松生成掩码,编译器可以在编译期间有效地解析这些掩码:
Naturally, using a binary to decimal converter is far from elegant, especially in the case of 32 and 64 bit containers - decimal values get crazy big. It is possible to easily generate the masks with expressions, which the compiler can efficiently resolve during compilation:
- 读取掩码和移位"掩码:
((1 <
,假设第一位的索引为1(非零) - 读取移位和掩码"的掩码:
(1 <
(索引在这里不起作用,因为它总是移到第一位 - 写掩码:只需反转掩码和移位";带有
~
运算符的掩码表达式
- read mask for "mask and shift":
((1 << fieldLength) - 1) << (fieldIndex - 1)
, assuming that the index at the first bit is 1 (not zero) - read mask for "shift and mask":
(1 << fieldLength) - 1
(index does not play a role here since it is always shifted to the first bit - write mask : just invert the "mask and shift" mask expression with the
~
operator
它是如何工作的(3bit 字段从上面示例中的第二位开始)?
How does it work (with the 3bit field beginning at the second bit from the examples above)?
00000001 << 3
00001000 - 1
00000111 << 1
00001110 ~ (read mask)
11110001 (write mask)
相同的示例适用于更宽的整数以及字段的任意位宽和位置,移位和掩码值相应地变化.
The same examples apply to wider integers and arbitrary bit width and position of the fields, with the shift and mask values varying accordingly.
另请注意,示例假定无符号整数,这是您想要使用的,以便将整数用作可移植的位域替代方案(标准绝不保证常规位域是可移植的),两者都是左并右移插入一个填充0,右移有符号整数不是这种情况.
Also note that the examples assume unsigned integer, which is what you want to use in order to use integers as portable bit-field alternative (regular bit-fields are in no way guaranteed by the standard to be portable), both left and right shift insert a padding 0, which is not the case with right shifting a signed integer.
更简单:
使用这组宏(但仅在 C++ 中,因为它依赖于成员函数的生成):
Using this set of macros (but only in C++ since it relies on the generation of member functions):
#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); }
你可以做一些简单的事情:
You could go for something as simple as:
struct A {
uint bitData;
FIELD(bitData, one, 0, 1)
FIELD(bitData, two, 1, 2)
};
并将位字段实现为您可以轻松访问的属性:
And have the bit fields implemented as properties you can easily access:
A a;
a.set_two(3);
cout << a.two();
用 gcc 的 typeof
pre-C++11 替换 decltype
.
Replace decltype
with gcc's typeof
pre-C++11.
相关文章