1ビットをどのように設定、クリア、および切り替えますか?

2008年09月07日に質問されました。  ·  閲覧回数 1.2M回  ·  ソース

JeffV picture
2008年09月07日

どのように少し設定、クリア、切り替えますか?

回答

Jeremy Ruten picture
2008年09月07日
3728

少し設定する

ビットごとのOR演算子( | )を使用してビットを設定します。

number |= 1UL << n;

これにより、 numbern番目のビットが設定されます。 n 1最初のビットを設定する場合はnをゼロにし、 n番目のビットを設定する場合はn-1まで続けます。

使用1ULL場合numberより広くなっているunsigned long ; プロモーション1UL << n評価した後までは発生しません1UL << n 、それはより多くの幅よりによってシフトに未定義の動作だところlong 。 同じことが残りのすべての例にも当てはまります。

少しクリア

ビットごとのAND演算子( & )を使用してビットをクリアします。

number &= ~(1UL << n);

これにより、 numbern番目のビットがクリアされます。 ビット文字列をビット単位のNOT演算子( ~ )で反転してから、ANDする必要があります。

少し切り替える

XOR演算子( ^ )を使用してビットを切り替えることができます。

number ^= 1UL << n;

これにより、 numbern番目のビットが切り替わります。

少しチェック

あなたはこれを求めていませんでしたが、私はそれを追加したほうがよいでしょう。

ビットを確認するには、数値nを右にシフトしてから、ビットごとにAND演算します。

bit = (number >> n) & 1U;

これにより、 numbern番目のビットの値が変数bitます。

n番目のビットをxに変更する

n番目のビットを1または0いずれかに設定するには、2の補数のC ++実装で次のようにします。

number ^= (-x ^ number) & (1UL << n);

ビットnは、 x1場合に設定され、 x0場合にクリアされます。 xに他の値がある場合、ゴミが発生します。 x = !!xはそれを0または1にブール化します。

これを2の補数の否定動作から独立させるには(1の補数または符号/大きさのC ++実装とは異なり、 -1にすべてのビットが設定されている場合)、符号なし否定を使用します。

number ^= (-(unsigned long)x ^ number) & (1UL << n);

または

unsigned long newbit = !!x;    // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);

ポータブルビット操作には、一般に符号なしタイプを使用することをお勧めします。

または

number = (number & ~(1UL << n)) | (x << n);

(number & ~(1UL << n))n番目のビットをクリアし、 (x << n)n番目のビットをxます。

また、一般的にコードをコピーして貼り付けないことも一般的には良い考えであり、多くの人がプリプロセッサマクロ(コミュニティウィキの回答など)または何らかのカプセル化を使用します。

Martin York picture
2008年09月18日
472

標準C ++ライブラリの使用: std::bitset<N>

またはBoostバージョン: boost::dynamic_bitset

あなた自身を転がす必要はありません:

#include <bitset>
#include <iostream>

int main()
{
    std::bitset<5> x;

    x[1] = 1;
    x[2] = 0;
    // Note x[0-4]  valid

    std::cout << x << std::endl;
}

[Alpha:] > ./a.out
00010

Boostバージョンでは、標準ライブラリのコンパイル時サイズのビットセットと比較して、ランタイムサイズのビットセットを使用できます。

Ferruccio picture
2008年09月11日
254

もう1つのオプションは、ビットフィールドを使用することです。

struct bits {
    unsigned int a:1;
    unsigned int b:1;
    unsigned int c:1;
};

struct bits mybits;

3ビットフィールドを定義します(実際には、3つの1ビットフィールドです)。 ビット演算が少し(笑)簡単になりました:

少し設定またはクリアするには:

mybits.b = 1;
mybits.c = 0;

少し切り替えるには:

mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1;  /* all work */

少し確認してください:

if (mybits.c)  //if mybits.c is non zero the next line below will execute

これは、固定サイズのビットフィールドでのみ機能します。 それ以外の場合は、以前の投稿で説明されているビットをいじるテクニックに頼る必要があります。

Steve Karg picture
2008年11月05日
193

ヘッダーファイルで定義されたマクロを使用して、ビットセットとクリアを処理します。

/* a=target variable, b=bit number to act upon 0-n */
#define BIT_SET(a,b) ((a) |= (1ULL<<(b)))
#define BIT_CLEAR(a,b) ((a) &= ~(1ULL<<(b)))
#define BIT_FLIP(a,b) ((a) ^= (1ULL<<(b)))
#define BIT_CHECK(a,b) (!!((a) & (1ULL<<(b))))        // '!!' to make sure this returns 0 or 1

/* x=target variable, y=mask */
#define BITMASK_SET(x,y) ((x) |= (y))
#define BITMASK_CLEAR(x,y) ((x) &= (~(y)))
#define BITMASK_FLIP(x,y) ((x) ^= (y))
#define BITMASK_CHECK_ALL(x,y) (!(~(x) & (y)))
#define BITMASK_CHECK_ANY(x,y) ((x) & (y))
dmckee --- ex-moderator kitten picture
2008年09月09日
119

enumを使用してビットに名前

enum ThingFlags = {
  ThingMask  = 0x0000,
  ThingFlag0 = 1 << 0,
  ThingFlag1 = 1 << 1,
  ThingError = 1 << 8,
}

その後、後で名前を使用し

thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}

設定、クリア、テストします。 このようにして、残りのコードから魔法数を非表示にします。

それ以外は、ジェレミーの解決策を支持します。

yogeesh picture
2008年09月17日
49

snip-c.zipのbitops.hから:

/*
**  Bit set, clear, and test operations
**
**  public domain snippet by Bob Stout
*/

typedef enum {ERROR = -1, FALSE, TRUE} LOGICAL;

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

OK、物事を分析しましょう...

これらすべてで問題があると思われる一般的な表現は、「(1L <<(posn))」です。 これは、シングルビットがオンのマスクを作成するだけで、任意の整数型で機能します。 「posn」引数は、ビットが必要な位置を指定します。 posn == 0の場合、この式は次のように評価されます。

0000 0000 0000 0000 0000 0000 0000 0001 binary.

posn == 8の場合、次のように評価されます。

0000 0000 0000 0000 0000 0001 0000 0000 binary.

つまり、指定された位置に1が付いた0のフィールドを作成するだけです。 唯一注意が必要なのは、BitClr()マクロで、1のフィールドに単一の0ビットを設定する必要があることです。 これは、チルダ(〜)演算子で示されるのと同じ式の1の補数を使用することによって実現されます。

マスクが作成されると、ビット単位のand(&)、または(|)、およびxor(^)演算子を使用して、提案したとおりに引数に適用されます。 マスクはlong型であるため、マクロはchar、short、int、またはlongでも同様に機能します。

肝心なのは、これが問題のクラス全体に対する一般的な解決策であるということです。 もちろん、これらのマクロのいずれかと同等のものを、必要になるたびに明示的なマスク値で書き直すことは可能であり、適切ですが、なぜそうするのでしょうか。 マクロの置換はプリプロセッサで行われるため、生成されたコードは、値がコンパイラによって一定であると見なされるという事実を反映することを忘れないでください。つまり、必要なたびに「車輪の再発明」を行うのと同じくらい一般化されたマクロを使用するのが効率的です。ビット操作を行います。

納得できませんか? ここにいくつかのテストコードがあります-私は完全に最適化され、_cdeclを使用せずにWatcom Cを使用したので、結果の逆アセンブリは可能な限りクリーンになります。

---- [TEST.C] --------------------------------------------------------- -----------------------

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

int bitmanip(int word)
{
      word = BitSet(word, 2);
      word = BitSet(word, 7);
      word = BitClr(word, 3);
      word = BitFlp(word, 9);
      return word;
}

---- [TEST.OUT(逆アセンブル)] -------------------------------------- ---------

Module: C:\BINK\tst.c
Group: 'DGROUP' CONST,CONST2,_DATA,_BSS

Segment: _TEXT  BYTE   00000008 bytes  
 0000  0c 84             bitmanip_       or      al,84H    ; set bits 2 and 7
 0002  80 f4 02                          xor     ah,02H    ; flip bit 9 of EAX (bit 1 of AH)
 0005  24 f7                             and     al,0f7H
 0007  c3                                ret     

No disassembly errors

---- [finis] ------------------------------------------- ----------------------

nsanders picture
2008年09月07日
39

ビット演算子を使用します: & |

000b最後のビットを設定するに

foo = foo | 001b

foo最後のビットをチェックするに

if ( foo & 001b ) ....

foo最後のビットをクリアするに

foo = foo & 110b

わかりやすくするためにXXXbを使用しました。 ビットをパックするデータ構造によっては、おそらくHEX表現を使用することになります。

kapilddit picture
2012年06月05日
35

初心者のために、例を挙げてもう少し説明したいと思います。

例:

value is 0x55;
bitnum : 3rd.

&演算子が使用され、ビットを確認します。

0101 0101
&
0000 1000
___________
0000 0000 (mean 0: False). It will work fine if the third bit is 1 (then the answer will be True)

トグルまたはフリップ:

0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)

|演算子:ビットを設定します

0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)
R.. GitHub STOP HELPING ICE picture
2010年07月13日
27

ここから符号なし整数配列のいずれかのタイプのために働く私のお気に入りのビット算術演算マクロ、だunsigned charまでのsize_t (と仕事への効率的であるべき最大のタイプであるが):

#define BITOP(a,b,op) \
 ((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))

少し設定するには:

BITOP(array, bit, |=);

少しクリアするには:

BITOP(array, bit, &=~);

少し切り替えるには:

BITOP(array, bit, ^=);

少しテストするには:

if (BITOP(array, bit, &)) ...

John U picture
2012年06月14日
26

これは「埋め込み」とタグ付けされているため、マイクロコントローラーを使用していると想定します。 上記の提案はすべて有効で機能します(読み取り-変更-書き込み、共用体、構造体など)。

ただし、オシロスコープベースのデバッグの試合中に、これらのメソッドは、マイクロのPORTnSET / PORTnCLEARレジスタに直接値を書き込む場合と比較して、CPUサイクルにかなりのオーバーヘッドがあり、タイトなループ/高が存在する場合に実際の違いが生じることに驚きました。 -周波数ISRのトグルピン。

なじみのない人のために:私の例では、マイクロには出力ピンを反映する一般的なピン状態レジスタPORTnがあるため、PORTn | = BIT_TO_SETを実行すると、そのレジスタへの読み取り-変更-書き込みが行われます。 ただし、PORTnSET / PORTnCLEARレジスタは、「このビットを1にしてください」(SET)または「このビットをゼロにしてください」(CLEAR)を意味する「1」と「ピンをそのままにしておく」を意味する「0」を取ります。 したがって、ビットを設定するかクリアするかに応じて2つのポートアドレスになりますが(必ずしも便利ではありません)、反応がはるかに速く、アセンブルされたコードが小さくなります。