假设我有一个二进制值为11111111的字节b例如,如何从第二位开始读取3位整数值,或从第五位开始写入4位整数值?
jckbn6z71#
一些2+年后,我问这个问题,我想解释它的方式,我希望它解释回来时,我仍然是一个完整的新手,将是最有益的人谁想要了解的过程。首先,忘记“11111111”示例值,它实际上并不完全适合于可视化地解释该过程,因此,将初始值设为10111011(十进制187),这将更好地说明该过程。
10111011
1 -如何从第二位开始读取3位值:
___ <- those 3 bits 10111011
该值为101,即十进制5,有2种可能的方法可以获取该值:
在这种方法中,首先用值00001110(十进制14)屏蔽所需的位,然后将其移位:
00001110
___ 10111011 AND 00001110 = 00001010 >> 1 = ___ 00000101
其表达式为:(value & 14) >> 1
(value & 14) >> 1
该方法类似,但操作顺序相反,即原始值被移位,然后使用00000111(7)进行掩码,仅留下最后3位:
00000111
___ 10111011 >> 1 ___ 01011101 AND 00000111 00000101
其表达式为:(value >> 1) & 7这两种方法都涉及相同的复杂性,因此在性能上没有差别。
(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)
(value & 241) | (6 << 1)
附加功能-如何生成读写掩码:
当然,使用二进制到十进制的转换器并不优雅,特别是在32位和64位容器的情况下-十进制值会变得非常大。可以很容易地用表达式生成掩码,编译器可以在编译过程中有效地解决这些问题:
((1 << fieldLength) - 1) << (fieldIndex - 1)
(1 << fieldLength) - 1
~
它是如何工作的(上面示例中的3位字段从第二位开始)?
00000001 << 3 00001000 - 1 00000111 << 1 00001110 ~ (read mask) 11110001 (write mask)
相同的例子适用于更宽的整数和任意位宽以及字段的位置,移位和掩码值相应地变化。还要注意的是,这些例子假设无符号整数,这是你想要使用的,以便使用整数作为可移植的位域替代(标准不保证常规的位域是可移植的),左移和右移都插入填充0,这不是右移有符号整数的情况。
更容易
使用这组宏(但仅在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); }
你可以选择一些简单的东西,比如:
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(C++11之前)。
decltype
typeof
jjjwad0x2#
你需要移动和屏蔽这个值,例如...如果要读取前两位,只需如下屏蔽它们:
int value = input & 0x3;
如果要偏移它,则需要右移N位,然后屏蔽掉所需的位:
int value = (intput >> 1) & 0x3;
就像你在问题中问的那样读三个比特。
int value = (input >> 1) & 0x7;
r6vfmomb3#
使用这些宏:
#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
mftmpeh84#
你必须做一个移位和掩码(AND)运算,令B是任意字节,p是你想要从中取n位(〉= 1)的位的索引(〉= 0)。首先,您必须向右移动**B*******次:
x = b >> p;
其次,必须用n个1来屏蔽结果:
mask = (1 << n) - 1; y = x & mask;
您可以将所有内容放入宏中:
#define TAKE_N_BITS_FROM(b, p, n) ((b) >> (p)) & ((1 << (n)) - 1)
c9qzyr3d5#
“例如,如何从第二位开始读取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,即实际上是第三位。)
jckbn6z76#
要读取字节,请使用std::bitset
const int bits_in_byte = 8; char myChar = 's'; cout << bitset<sizeof(myChar) * bits_in_byte>(myChar);
要编写,需要使用按位运算符,如& ^|&〈〈〉〉.一定要学会他们是做什么的。例如,要有00100100,你需要设置第一位为1,并移动它与〈〈〉〉操作符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);
a8jjtwal7#
int x = 0xFF; //your number - 11111111
例如,如何从第二位开始读取3位整数值
int y = x & ( 0x7 << 2 ) // 0x7 is 111 // and you shift it 2 to the left
66bbxpm58#
如果你一直从数据中获取比特,你可能需要使用比特域,你只需要设置一个结构体,并且只加载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上工作,而不是arm,因为arm可以是大字节序或小字节序。
8条答案
按热度按时间jckbn6z71#
一些2+年后,我问这个问题,我想解释它的方式,我希望它解释回来时,我仍然是一个完整的新手,将是最有益的人谁想要了解的过程。
首先,忘记“11111111”示例值,它实际上并不完全适合于可视化地解释该过程,因此,将初始值设为
10111011
(十进制187),这将更好地说明该过程。1 -如何从第二位开始读取3位值:
该值为101,即十进制5,有2种可能的方法可以获取该值:
在这种方法中,首先用值
00001110
(十进制14)屏蔽所需的位,然后将其移位:其表达式为:
(value & 14) >> 1
该方法类似,但操作顺序相反,即原始值被移位,然后使用
00000111
(7)进行掩码,仅留下最后3位:其表达式为:
(value >> 1) & 7
这两种方法都涉及相同的复杂性,因此在性能上没有差别。
2 -如何从第二位开始写入3位值:
在这种情况下,初始值是已知的,当代码中出现这种情况时,您可能能够想出一种方法,将已知值设置为另一个已知值,该方法使用较少的运算,但实际上很少出现这种情况,大多数情况下,代码既不知道初始值,也不知道要编写的值。
这意味着,为了将新值成功“拼接”成字节,必须将目标位设置为零,然后将移位后的值“拼接”到位,这是第一步:
第二步是将我们要写入3位的值移位,例如,我们要将其从101(5)更改为110(6)
第三步也是最后一步是将屏蔽的原始值与移位后的“拼接”值进行拼接:
整个过程的表达式为:
(value & 241) | (6 << 1)
附加功能-如何生成读写掩码:
当然,使用二进制到十进制的转换器并不优雅,特别是在32位和64位容器的情况下-十进制值会变得非常大。可以很容易地用表达式生成掩码,编译器可以在编译过程中有效地解决这些问题:
((1 << fieldLength) - 1) << (fieldIndex - 1)
,假设第一位的索引为1(非零)(1 << fieldLength) - 1
(索引在这里不起作用,因为它总是移位到第一位~
运算符反转“mask and shift”掩码表达式它是如何工作的(上面示例中的3位字段从第二位开始)?
相同的例子适用于更宽的整数和任意位宽以及字段的位置,移位和掩码值相应地变化。
还要注意的是,这些例子假设无符号整数,这是你想要使用的,以便使用整数作为可移植的位域替代(标准不保证常规的位域是可移植的),左移和右移都插入填充0,这不是右移有符号整数的情况。
更容易
使用这组宏(但仅在C++中使用,因为它依赖于成员函数的生成):
你可以选择一些简单的东西,比如:
并将位字段实现为您可以轻松访问的属性:
将
decltype
替换为gcc的typeof
(C++11之前)。jjjwad0x2#
你需要移动和屏蔽这个值,例如...
如果要读取前两位,只需如下屏蔽它们:
如果要偏移它,则需要右移N位,然后屏蔽掉所需的位:
就像你在问题中问的那样读三个比特。
r6vfmomb3#
使用这些宏:
示例用法:
mftmpeh84#
你必须做一个移位和掩码(AND)运算,令B是任意字节,p是你想要从中取n位(〉= 1)的位的索引(〉= 0)。
首先,您必须向右移动**B*******次:
其次,必须用n个1来屏蔽结果:
您可以将所有内容放入宏中:
c9qzyr3d5#
“例如,如何从第二位开始读取3位整数值?”
(我假设“第二位”是位#2,即实际上是第三位。)
jckbn6z76#
要读取字节,请使用std::bitset
要编写,需要使用按位运算符,如& ^|&〈〈〉〉.一定要学会他们是做什么的。
例如,要有00100100,你需要设置第一位为1,并移动它与〈〈〉〉操作符5次。如果你想继续写你只是继续设置第一位,并移动它。这很像一个旧打字机:你写字,然后换纸。
对于00100100:将第一位设置为1,移位5次,将第一位设置为1,移位2次:
a8jjtwal7#
例如,如何从第二位开始读取3位整数值
66bbxpm58#
如果你一直从数据中获取比特,你可能需要使用比特域,你只需要设置一个结构体,并且只加载1和0:
然后稍后像这样加载它(用int或任何你正在加载的数据替换char):
然后访问元素:
或
所有这些都是假设在i86/64上工作,而不是arm,因为arm可以是大字节序或小字节序。