假设我有一个字节b,其二进制值为11111111
我如何读取从第二位开始的3位整数值或从第五位开始写一个四位整数值?
答案 0 :(得分:101)
在我提出这个问题大约2年多之后,我想解释一下,当我还是一个完整的新手时,我想要解释它的方式,并且对那些想要理解这个过程的人最有益。 / p>
首先,忘记“11111111”示例值,它实际上并不适用于流程的可视化解释。因此,让初始值为10111011
(十进制187),这将更加说明过程。
1 - 如何从第二位开始读取3位值:
___ <- 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 - 如何从第二位开始写入3位值:
在这种情况下,初始值是已知的,当代码中出现这种情况时,您可能想出一种方法将已知值设置为另一个使用较少操作的已知值,但实际上这是在很少的情况下,大多数情况下代码既不知道初始值,也不知道要写的那个。
这意味着为了将新值成功地“拼接”到字节中,必须将目标位设置为零,然后将移位的值“拼接”到位,这是第一步:
___
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)
奖励 - 如何生成读写掩码:
当然,使用二进制到十进制转换器远非优雅,特别是在32位和64位容器的情况下 - 十进制值变得疯狂。可以使用表达式轻松生成掩码,编译器可以在编译期间有效地解析这些表达式:
((1 << fieldLength) - 1) << (fieldIndex - 1)
,假设第一位的索引为1(非零)(1 << fieldLength) - 1
(索引在这里不起作用,因为它总是转移到第一位~
运算符它是如何工作的(3比特字段从上面例子的第二位开始)?
00000001 << 3
00001000 - 1
00000111 << 1
00001110 ~ (read mask)
11110001 (write mask)
相同的例子适用于更宽的整数和字段的任意位宽和位置,移位和掩码值也相应变化。
另请注意,这些示例假定无符号整数,这是您想要使用的整数作为便携式位域替代方案(标准无法保证常规位字段可移植),两者都是右移插入填充0,右移有符号整数不是这种情况。
更简单:
使用这组宏(但仅限于C ++,因为它依赖于成员函数的生成):
#define GETMASK(index, size) (((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)))
#define FIELD(data, name, index, size) \
inline decltype(data) name() { return READFROM(data, index, size); } \
inline void set_##name(decltype(data) value) { WRITETO(data, index, size, value); }
你可以选择一些简单的事情:
struct A {
uint bitData;
FIELD(bitData, one, 0, 1)
FIELD(bitData, two, 1, 2)
};
将位字段实现为您可以轻松访问的属性:
A a;
a.set_two(3);
cout << a.two();
将decltype
替换为gcc的typeof
pre-C ++ 11。
答案 1 :(得分:13)
您需要移动并屏蔽该值,例如......
如果你想读取前两位,你只需要将它们屏蔽掉:
int value = input & 0x3;
如果你想要偏移它,你需要右移N位,然后屏蔽你想要的位:
int value = (intput >> 1) & 0x3;
按照你在问题中提到的那样读三位。
int value = (input >> 1) & 0x7;
答案 2 :(得分:6)
您必须执行移位和遮罩(AND)操作。 设 b 为任意字节, p 为您要从中获取 n 位的位的索引(&gt; = 0)(&gt; ; = 1)。
首先,您必须按 p 时间右移 b :
x = b >> p;
其次,您必须使用 n :
来掩盖结果mask = (1 << n) - 1;
y = x & mask;
您可以将所有内容放在宏中:
#define TAKE_N_BITS_FROM(b, p, n) ((b) >> (p)) & ((1 << (n)) - 1)
答案 3 :(得分:3)
“我如何读取从第二位开始的3位整数值?”
int number = // whatever;
uint8_t val; // uint8_t is the smallest data type capable of holding 3 bits
val = (number & (1 << 2 | 1 << 3 | 1 << 4)) >> 2;
(我假设“第二位”是位#2,即第三位真的。)
答案 4 :(得分:3)
只需使用此功能并且无忧无虑:
#define BitVal(data,y) ( (data>>y) & 1) /** Return Data.Y value **/
#define SetBit(data,y) data |= (1 << y) /** Set Data.Y to 1 **/
#define ClearBit(data,y) data &= ~(1 << y) /** Clear Data.Y to 0 **/
#define TogleBit(data,y) (data ^=BitVal(y)) /** Togle Data.Y value **/
#define Togle(data) (data =~data ) /** Togle Data value **/
例如:
uint8_t number = 0x05; //0b00000101
uint8_t bit_2 = BitVal(number,2); // bit_2 = 1
uint8_t bit_1 = BitVal(number,1); // bit_1 = 0
SetBit(number,1); // number = 0x07 => 0b00000111
ClearBit(number,2); // number =0x03 => 0b0000011
答案 5 :(得分:2)
要读取字节,请使用std :: bitset
const int bits_in_byte = 8;
char myChar = 's';
cout << bitset<sizeof(myChar) * bits_in_byte>(myChar);
要编写,您需要使用按位运算符,例如&amp; ^ | &安培; &LT;&LT; &GT;取代。确保了解他们的所作所为。
例如要拥有00100100,您需要将第一位设置为1,并使用&lt;&lt; &GT;&GT;运营商5次。如果你想继续写,你只需继续设置第一位并移动它。它非常像一台旧打字机:你写,转移纸张。
对于00100100:将第一位设置为1,将第一位设置为5,将第一位设置为1,并将第2位设置为:
const int bits_in_byte = 8;
char myChar = 0;
myChar = myChar | (0x1 << 5 | 0x1 << 2);
cout << bitset<sizeof(myChar) * bits_in_byte>(myChar);
答案 6 :(得分:1)
int x = 0xFF; //your number - 11111111
我如何从第二位开始读取3位整数值
int y = x & ( 0x7 << 2 ) // 0x7 is 111
// and you shift it 2 to the left
答案 7 :(得分:0)
如果你继续抓取数据中的位,你可能想要使用位域。您只需设置一个结构并仅使用1和0加载它:
struct bitfield{
unsigned int bit : 1
}
struct bitfield *bitstream;
然后稍后加载它(用int替换char或你正在加载的任何数据):
long int i;
int j, k;
unsigned char c, d;
bitstream=malloc(sizeof(struct bitfield)*charstreamlength*sizeof(char));
for (i=0; i<charstreamlength; i++){
c=charstream[i];
for(j=0; j < sizeof(char)*8; j++){
d=c;
d=d>>(sizeof(char)*8-j-1);
d=d<<(sizeof(char)*8-1);
k=d;
if(k==0){
bitstream[sizeof(char)*8*i + j].bit=0;
}else{
bitstream[sizeof(char)*8*i + j].bit=1;
}
}
}
然后访问元素:
bitstream[bitpointer].bit=...
或
...=bitstream[bitpointer].bit
所有这一切都假设是在i86 / 64上工作,而不是手臂,因为手臂可以是大端或小端。