如何在 C/C++ 中读/写任意位

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11815894/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 15:35:09  来源:igfitidea点击:

How to read/write arbitrary bits in C/C++

c++cmemorybitread-write

提问by dtech

Assuming I have a byte b with the binary value of 11111111

假设我有一个二进制值为 11111111 的字节 b

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?

例如,如何从第二位开始读取 3 位整数值或从第五位开始写入四位整数值?

回答by dtech

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.

在我问这个问题大约 2 年多之后,我想以我希望在我还是一个完整的新手时解释它的方式来解释它,这对想要了解这个过程的人来说是最有益的。

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.

首先,忘记“11111111”示例值,它并不是真正适合对过程进行可视化解释的全部内容。所以让初始值是10111011(187 十进制),这将更能说明这个过程。

1 - how to read a 3 bit value starting from the second bit:

1 - 如何从第二位开始读取 3 位值:

    ___  <- those 3 bits
10111011 

The value is 101, or 5 in decimal, there are 2 possible ways to get it:

该值是 101,或十进制 5,有 2 种可能的方法来获取它:

  • mask and shift
  • 掩码和移位

In this approach, the needed bits are first masked with the value 00001110(14 decimal) after which it is shifted in place:

在这种方法中,需要的位首先用值00001110(十进制 14)进行掩码,然后将其移位到位:

    ___
10111011 AND
00001110 =
00001010 >> 1 =
     ___
00000101

The expression for this would be: (value & 14) >> 1

表达式为: (value & 14) >> 1

  • shift and mask
  • 移位和掩码

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:

这种方法类似,但操作顺序相反,这意味着原始值被移位,然后用00000111(7)屏蔽,只留下最后 3 位:

    ___
10111011 >> 1
     ___
01011101 AND
00000111
00000101

The expression for this would be: (value >> 1) & 7

表达式为: (value >> 1) & 7

Both approaches involve the same amount of complexity, and therefore will not differ in performance.

两种方法都涉及相同的复杂性,因此在性能上不会有所不同。

2 - how to write a 3 bit value starting from the second bit:

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)

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)

第二步是将我们要写入的值移动 3 位,假设我们要将其从 101 (5) 更改为 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

The expression for the whole process would be: (value & 241) | (6 << 1)

整个过程的表达式为: (value & 241) | (6 << 1)

Bonus - how to generate the read and write masks:

奖励 - 如何生成读写掩码:

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:

自然地,使用二进制到十进制转换器远非优雅,尤其是在 32 位和 64 位容器的情况下 - 十进制值变得非常大。可以使用表达式轻松生成掩码,编译器可以在编译期间有效地解析这些掩码:

  • 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
  • read mask for "mask and shift": ((1 << fieldLength) - 1) << (fieldIndex - 1),假设第一位的索引为 1(非零)
  • 读取“移位和掩码”的掩码:((1 << fieldLength) - 1索引在这里不起作用,因为它总是移到第一位
  • 写掩码:只需使用~运算符反转“掩码和移位”掩码表达式

How does it work (with the 3bit field beginning at the second bit from the examples above)?

它是如何工作的(3bit 字段从上面示例中的第二位开始)?

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.

相同的示例适用于更宽的整数以及字段的任意位宽和位置,移位和掩码值相应地变化。

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.

另请注意,示例假定无符号整数,这是您想要使用的,以便将整数用作可移植位域替代(标准位域绝不保证是可移植的),左移和右移插入一个填充 0,右移有符号整数不是这种情况。

Even easier:

更简单:

Using this set of macros (but only in C++ since it relies on the generation of member functions):

使用这组宏(但仅在 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); }

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();

Replace decltypewith gcc's typeofpre-C++11.

替换decltype为 gcc 的typeofpre-C++11。

回答by Geoffrey

You need to shift and mask the value, so for example...

您需要移动并屏蔽该值,例如...

If you want to read the first two bits, you just need to mask them off like so:

如果你想读取前两位,你只需要像这样屏蔽它们:

int value = input & 0x3;

If you want to offset it you need to shift right N bits and then mask off the bits you want:

如果你想抵消它,你需要右移 N 位,然后屏蔽你想要的位:

int value = (intput >> 1) & 0x3;

To read three bits like you asked in your question.

阅读您在问题中问的三个位。

int value = (input >> 1) & 0x7;

回答by Hamid

just use this and feelfree:

只需使用它即可:

#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     **/

for example:

例如:

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

回答by Claudix

You have to do a shift and mask (AND) operation. Let bbe any byte and pbe the index (>= 0) of the bit from which you want to take nbits (>= 1).

您必须执行移位和掩码 (AND) 操作。让b是任何字节,p是您想要从中获取n位 (>= 1)的位的索引 (>= 0 )。

First you have to shift right bby ptimes:

首先,你必须右移bp倍:

x = b >> p;

Second you have to mask the result with nones:

其次,你必须用n来掩盖结果:

mask = (1 << n) - 1;
y = x & mask;

You can put everything in a macro:

你可以把所有东西都放在一个宏中:

#define TAKE_N_BITS_FROM(b, p, n) ((b) >> (p)) & ((1 << (n)) - 1)

回答by Claudix

"How do I for example read a 3 bit integer value starting at the second bit?"

“例如,我如何从第二位开始读取 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;

(I assumed that "second bit" is bit #2, i. e. the third bit really.)

(我假设“第二位”是位#2,即实际上是第三位。)

回答by MasterMastic

To read bytes use std::bitset

要读取字节使用 std::bitset

const int bits_in_byte = 8;

char myChar = 's';
cout << bitset<sizeof(myChar) * bits_in_byte>(myChar);

To write you need to use bit-wise operators such as & ^ | & << >>. make sure to learn what they do.

要写入,您需要使用按位运算符,例如 & ^ | &<<>>。一定要学习他们做什么。

For example to have 00100100 you need to set the first bit to 1, and shift it with the << >> operators 5 times. if you want to continue writing you just continue to set the first bit and shift it. it's very much like an old typewriter: you write, and shift the paper.

例如,要获得 00100100,您需要将第一位设置为 1,然后使用 << >> 运算符将其移位 5 次。如果你想继续写,你只需继续设置第一位并移动它。它非常像一台旧打字机:你写字,然后移动纸张。

For 00100100: set the first bit to 1, shift 5 times, set the first bit to 1, and shift 2 times:

对于 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);

回答by Luchian Grigore

int x = 0xFF;   //your number - 11111111

How do I for example read a 3 bit integer value starting at the second bit

例如,我如何读取从第二位开始的 3 位整数值

int y = x & ( 0x7 << 2 ) // 0x7 is 111
                         // and you shift it 2 to the left

回答by Dominic Mason

If you keep grabbing bits from your data, you might want to use a bitfield. You'll just have to set up a struct and load it with only ones and zeroes:

如果您不断从数据中获取位,则可能需要使用位域。您只需要设置一个结构并仅使用 1 和 0 加载它:

struct bitfield{
    unsigned int bit : 1
}
struct bitfield *bitstream;

then later on load it like this (replacing char with int or whatever data you are loading):

然后稍后像这样加载它(用 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;
        }
    }
}

Then access elements:

然后访问元素:

bitstream[bitpointer].bit=...

or

或者

...=bitstream[bitpointer].bit

All of this is assuming are working on i86/64, not arm, since arm can be big or little endian.

所有这些都假设在 i86/64 上工作,而不是在 arm 上工作,因为 arm 可以是大端或小端。