web-dev-qa-db-ja.com

C / C ++で任意のビットを読み書きする方法

11111111のバイナリ値を持つバイトbがあると仮定します

たとえば、2番目のビットから始まる3ビットの整数値を読み取る方法、または5番目のビットから始まる4ビットの整数値を書き込む方法を教えてください。

30
dtech

この質問を行ってから2年後、私はまだ完全な初心者であり、プロセスを理解したい人々にとって最も有益な方法であると説明したい方法で説明したいと思います。

まず、「11111111」の値の例を忘れてください。これは、プロセスの視覚的な説明に実際に適しているわけではありません。そのため、初期値を10111011(10進数で187)にして、プロセスをもう少し説明します。

1-2番目のビットから3ビット値を読み取る方法:

    ___  <- those 3 bits
10111011 

値は101、または10進数で5です。取得する方法は2つあります。

  • マスクとシフト

このアプローチでは、必要なビットが最初に値00001110(10進数で14)でマスクされ、その後、適切な位置にシフトされます。

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

この式は次のようになります。(value & 14) >> 1

  • シフトとマスク

このアプローチは似ていますが、操作の順序が逆になります。つまり、元の値がシフトされ、00000111(7)でマスクされて最後の3ビットのみが残されます。

    ___
10111011 >> 1
     ___
01011101 AND
00000111
00000101

この式は次のようになります。(value >> 1) & 7

どちらのアプローチも同じ量の複雑さを伴うため、パフォーマンスに違いはありません。

2-2番目のビットから始まる3ビットの値を書き込む方法:

この場合、初期値は既知であり、これがコードの場合、既知の値をより少ない操作を使用する別の既知の値に設定する方法を考え出すことができますが、実際にはこれはめったにありませんほとんどの場合、コードは初期値も書き込まれる値も認識しません。

これは、新しい値をバイトに正常に「スプライス」するために、ターゲットビットをゼロに設定する必要があることを意味します。その後、シフトされた値が所定の位置に「スプライス」されます。

    ___ 
10111011 AND
11110001 (241) =
10110001 (masked original value)

2番目のステップでは、3ビットで書き込む値をシフトします。たとえば、101(5)から110(6)に変更します。

     ___
00000110 << 1 =
    ___
00001100 (shifted "splice" value)

3番目の最後のステップは、マスクされた元の値をシフトされた「スプライス」値でスプライスすることです。

10110001 OR
00001100 =
    ___
10111101

プロセス全体の式は次のようになります。(value & 241) | (6 << 1)

ボーナス-読み取りおよび書き込みマスクの生成方法:

当然、特に32ビットおよび64ビットコンテナーの場合、バイナリから10進数へのコンバーターの使用はエレガントとはほど遠い-10進数の値は非常に大きくなります。式を使用してマスクを簡単に生成することができます。これは、コンパイラーがコンパイル中に効率的に解決できます。

  • 「マスクとシフト」の読み取りマスク:((1 << fieldLength) - 1) << (fieldIndex - 1)、最初のビットのインデックスが1(ゼロではない)であると仮定
  • 「シフトとマスク」の読み取りマスク:(1 << fieldLength) - 1(インデックスは常に最初のビットにシフトされるため、ここではインデックスは役割を果たしません。
  • マスクの書き込み:~演算子を使用して、「マスクとシフト」マスク式を反転します

どのように機能しますか(上記の例の2ビット目から始まる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に置き換えます。

103
dtech

値をシフトしてマスクする必要があるため、たとえば...

最初の2ビットを読みたい場合は、次のようにマスクするだけです。

int value = input & 0x3;

オフセットする場合は、右Nビットにシフトしてから、必要なビットをマスクする必要があります。

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

質問で尋ねたように3ビットを読む。

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

シフトとマスク(AND)操作を行う必要があります。 bを任意のバイトとし、pを取得するビットのインデックス(> = 0)としますn bits(> = 1)。

まず、右にシフトする必要がありますb by p回:

x = b >> p;

次に、結果をn onesでマスクする必要があります。

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

すべてをマクロに入れることができます:

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

これを使用して、気軽に:

#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
4
Hamid

「たとえば、2番目のビットから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番目のビット」はビット#2、つまり3番目のビットであると想定しました。)

3
user529758

バイトを読み取るには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);
2
MasterMastic
int x = 0xFF;   //your number - 11111111

たとえば、2番目のビットから始まる3ビットの整数値を読み取るにはどうすればよいですか

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

データからビットを取得し続ける場合は、ビットフィールドを使用することをお勧めします。構造体をセットアップし、1と0のみでロードする必要があります。

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

その後、このようにロードします(charをintまたはロードするデータに置き換えます):

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で動作していると仮定しています。

0
Dominic Mason