シングルビットをどのように設定、クリア、トグルしますか?


2569

どのように少し設定、クリア、トグルしますか?


65
これを読んでください:graphics.stanford.edu/~seander/bithacks.htmlそして、これをマスターするときは、これを読んでください:realtimecollisiondetection.net/blog/
p

9
また、Bit TwiddlerBit Twiddling HacksAggregate Magic Algorithmsをチェックすることに興味があるかもしれません。

回答:


3599

ビットを設定する

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

number |= 1UL << n;

これにより、のnthビットが設定されnumberます。stビットnを設定する場合、1stビットを設定する場合は0 までn-1nthビットを設定する場合は0にする必要があります。

使用1ULL場合は、numberより広いですunsigned long。のプロモーションは、の幅を超えてシフトすることが未定義の動作である場所1UL << nを評価するまで発生しません。残りのすべての例にも同じことが当てはまります。1UL << nlong

少しクリア

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

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

これnにより、のthビットがクリアされnumberます。ビット文字列は、ビットごとのNOT演算子(~)を使用して反転してから、ANDする必要があります。

少し切り替える

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

number ^= 1UL << n;

これnにより、のth番目のビットが切り替わりますnumber

少しチェック

あなたはこれを要求しませんでしたが、私はそれを追加することもできます。

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

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

これにより、のnth番目のビットの値がnumber変数に入れられますbit

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

設定nのいずれかにビット目を1または02の補数のC ++実装に次のように達成することができます:

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

ビットがnあれば設定されxている1、とあればクリアxです0。場合はx、いくつかの他の値を持っている、あなたはゴミを取得します。 x = !!x0または1にブール値化します。

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

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))nthビットをクリアし、thビットを(x << n)に設定nxます。

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


128
ビットセット/クリアをネイティブサポートしているプラ​​ットフォーム(例:AVRマイクロコントローラー)では、コンパイラーはしばしばxがいつでも 'myByte | =(1 << x)'をネイティブビットセット/クリア命令に変換することに注意してください定数、例:(1 << 5)、またはconst unsigned x = 5
Aaron

52
ビット=数値&(1 << x); ビットの型が_Bool(<stdbool.h>)でない限り、ビットxの値をビットに入れません。それ以外の場合、ビット= !!(数値&(1 << x)); will ..
クリス・ヤング、

23
最後の1つを次のように変更しないでくださいbit = (number >> x) & 1
アーロンマン2013年

42
1int署名されたリテラルです。したがって、ここでのすべての操作は、標準では十分に定義されていない、署名された数値で動作します。標準は2の補数または算術シフトを保証しないため、を使用することをお勧めします1U
Siyuan Ren

50
number = number & ~(1 << n) | (x << n);はn番目のビットをxに変更することを好みます。
jiasli 2015年

459

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


34
+1。そのstd :: bitsetは "C"からは使用できませんが、著者が質問に "C ++"、AFAIKのタグを付けたので、あなたの答えはここで一番です... std :: vector <bool>は別の方法です、その長所と短所を知っているなら
パルセバル

23
@andrewdotnich:vector <bool>は(残念ながら)値をビットとして格納する特殊化です。詳細はgotw.ca/publications/mill09.htmを参照してください...
Niklas

71
これは埋め込まれたタグが付けられたため、おそらく誰もそれを言及しませんでした。ほとんどの組み込みシステムでは、ペストのようなSTLを回避します。そして、ブーストのサポートは、ほとんどの組み込みコンパイラーのなかで見つけられる非常にまれな鳥です。
ランディン

17
@Martinそれは本当です。多くの組み込みシステムは、STLやテンプレートなどの特定のパフォーマンスキラーに加えて、標準ライブラリ全体を完全に回避しています。組み込みブランチのほとんどはMISRAのような標準を採用しており、静的コード分析ツールが必要です(ソフトウェア専門家は、組み込みの人々だけでなく、そのようなツールを使用する必要があります)。一般に、ソースコードが特定のコンパイラで利用できる場合でも、標準ライブラリ全体で静的分析を実行するよりも、やるべきことはたくさんあります。
ランディン2011

37
@Lundin:あなたの発言は過度に広範です(したがって、議論するのは無意味です)。私はそれらが真実であれば状況を見つけることができると確信しています。これは私の最初のポイントを変更しません。これらのクラスはどちらも、組み込みシステムでの使用にはまったく問題ありません(私はそれらが使用されているという事実を知っています)。組み込みシステムで使用されていないSTL / Boostに関する最初のポイントも間違っています。それらを使用しないシステムや、それらを使用するシステムでさえ、慎重に使用されているシステムがあると確信していますが、使用されていないと言うのは正しくありません(使用されているシステムがあるためです)。
マーティンヨーク

248

もう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

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


68
私はいつもビットフィールドを使うのは悪い考えだと思っていました。ビットが割り当てられる順序(上または下から)を制御することはできません。そのため、一度に1つのビットを除いて、安定した/移植可能な方法で値をシリアル化することはできません。DIYビット演算とビットフィールドを混在させることも不可能です。たとえば、一度に複数のビットをテストするマスクを作成します。もちろん&&を使用して、コンパイラーがそれを正しく最適化することを願っています...
R .. GitHub ICE HELPING ICE

34
ビットフィールドは多くの点で悪いので、私はそれについて本を書くことがほとんどできました。実際、MISRA-Cへの準拠を必要とするビットフィールドプログラムでは、ほとんどそれを行わなければなりませんでした。MISRA-Cは、実装で定義されたすべての動作を文書化することを強制するため、ビットフィールドで問題が発生する可能性のあるすべてについてかなりのエッセイを書いてしまいました。ビット順序、エンディアン、パディングビット、パディングバイト、その他のさまざまな配置の問題、ビットフィールドとの暗黙的および明示的な型変換、intが使用されていない場合のUBなど。代わりに、バグや移植可能なコードを減らすためにビットごとの演算子を使用してください。ビットフィールドは完全に冗長です。
ランディン2011

44
ほとんどの言語機能と同様に、ビットフィールドは正しく使用することも、悪用することもできます。複数の小さな値を1つのintにパックする必要がある場合、ビットフィールドが非常に役立ちます。一方、ビットフィールドが実際の包含intにどのようにマッピングされるかについて想定し始めた場合は、問題を求めているだけです。
Ferruccio 2011

4
@endolith:それは良い考えではないでしょう。機能させることもできますが、必ずしも別のプロセッサ、別のコンパイラ、または同じコンパイラの次のリリースに移植できるとは限りません。
Ferruccio

3
@YaskyとFerruccioがこのアプローチでsizeof()に対して異なる答えを得ると、コンパイラだけでなくハードウェア全体の互換性に関する問題が明らかになります。言語や定義済みのランタイムでこれらの問題を解決したと自分自身をだますことがありますが、それは本当に「自分のマシンで機能するか」ということになります。あなたが埋め込まれた男は私の尊敬(そして同情)を持っています。
ケリーS.フランス語

181

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

/* 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)) == (y))   // warning: evaluates y twice
#define BITMASK_CHECK_ANY(x,y) ((x) & (y))

17
ええと、これは5年前の投稿ですが、これらのマクロのいずれにも引数の重複はありません。ダン
ロバートケリー

11
BITMASK_CHECK(x,y) ((x) & (y))((x) & (y)) == (y)それ以外の場合は、マルチビットマスクで不正な結果を返す必要があります(例:5vs . 3)/ *すべてのgravediggersに挨拶する:)* /
brigadir

7
1(uintmax_t)1誰かがこれらのマクロを1 longつ以上のタイプで使用しようとした場合、または類似している必要があります
MM

2
BITMASK_CHECK_ALL(x,y)として実装可能!~((~(y))|(x))
Handy999

3
@ Handy999 De Morganの法則を適用し、取得のために再調整した後、それが機能する理由を理解するのは少し簡単です!(~(x) & (y))
Tavian Barnes

114

ビットenum名前付けるためにを使用する価値がある場合があります。

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

その後、名前を使用します。すなわち書く

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

設定、クリア、テスト。このようにして、残りのコードからマジックナンバーを隠します。

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


1
あるいは、のclearbits()代わりに関数を作成することもできます&= ~。なぜこれに列挙型を使用しているのですか?それらは任意の値が隠された一連の一意の変数を作成するためのものだと思いましたが、それぞれに明確な値を割り当てています。では、変数として定義するだけの利点は何でしょうか?
内部石

4
@endolith:enum関連する定数のセットにsを使用することは、cプログラミングではかなり前に遡ります。最近のコンパイラーでは、const short明示的にグループ化されていることの唯一の利点、またはそれ以上の利点があると思います。そして、ビットマスク以外のものためにそれらを必要とするとき、あなたは自動ナンバリングを得ます。もちろん、C ++では、それらは別個の型も形成し、静的なエラーチェックを少し追加します。
dmckee ---元モデレーターの子猫

ビットの可能な値のそれぞれに対して定数を定義しないと、未定義の列挙型定数に入るでしょう。たとえばのenum ThingFlags値は何ThingError|ThingFlag1ですか?
Luis Colorado

6
このメソッドを使用する場合、列挙型定数は常に符号付き型であることに注意してくださいint。これは、暗黙の整数の昇格または符号付きの型に対するビットごとの演算のため、あらゆる種類の微妙なバグを引き起こす可能性があります。thingstate = ThingFlag1 >> 1たとえば、実装定義の動作を呼び出します。thingstate = (ThingFlag1 >> x) << y未定義の動作を呼び出すことができます。等々。安全のため、常に符号なしの型にキャストしてください。
ランディン

1
@Lundin:C ++ 11以降では、列挙型の基になるタイプを設定できます。例: enum My16Bits: unsigned short { ... };
Aiken Drum

47

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

では、分析してみましょう...

これらすべてに問題があると思われる一般的な表現は「(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の補数を使用することで実現されます。

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

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

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

---- [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] ------------------------------------------- ----------------------


3
これに関する2つのこと:(1)マクロを熟読する際に、マクロが実際にargのビットを設定/クリア/フリップするものと誤って信じる人もいますが、割り当てはありません。(2)test.cが不完全です。さらに多くのケースを実行した場合、問題を見つけるでしょう(読者演習)
Dan

19
-1これは奇妙な難読化です。マクロの背後に言語構文を隠蔽してC言語を再発明しないでください。これは非常に悪い習慣です。次に、いくつかの奇妙な点があります。最初に、1Lは符号付きです。つまり、すべてのビット演算は符号付きの型で実行されます。これらのマクロに渡されたすべてのものは、署名されたlongとして返されます。良くない。第2に、これは、操作がintレベルであった可能性がある場合に長く実行されるため、小さなCPUでは非常に非効率的に機能します。第三に、関数のようなマクロはすべての悪の根源です。型の安全性はまったくありません。また、割り当てがないという前のコメントは非常に有効です。
ランディン2011

2
場合、これは失敗しますargですlong long1Lそう、可能な限り広いタイプである必要があります(uintmax_t)1。(あなたはで逃げるかもしれません1ull
MM

コードサイズを最適化しましたか?IntelメインストリームCPUでは、この関数が戻った後にAXまたはEAXを読み取ると、部分レジスタストールが発生します。これは、EAXの8ビットコンポーネントを書き込むためです。(AMD CPU、または完全なレジスタとは別に部分的なレジスタの名前を変更しない他のCPUでは問題ありません 。Haswell/ SkylakeはALを個別に名前変更しませんが、AHの名前を変更します。
Peter Cordes

37

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

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

foo = foo | 001b

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

if ( foo & 001b ) ....

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

foo = foo & 110b

XXXbわかりやすくするために使用しました。ビットをパックするデータ構造に応じて、おそらくHEX表現で作業します。


7
Cには2進表記はありません。2進整数定数は非標準の拡張です。
ランディン

XORを使用してビットを切り替える:foo = foo ^ MY_MASK
Peter L

明確に行うためにマスクを反転する使用しない:foo = foo & ~MY_MASK
ピーター・L

32

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

例:

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)

26

ここから符号なし整数配列のいずれかのタイプのために働く私のお気に入りのビット算術演算マクロ、です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, &)) ...


5
読むのは良いですが、起こりうる副作用に注意する必要があります。BITOP(array, bit++, |=);ループで使用すると、呼び出し元が望んでいることはほとんど行われません。
2010

確かに。あなたが好むかもしれない=)一つの変異体は、配列要素、所定の位置にビットをシフトするための他に対処するための2つのマクロ、1に分離することで、ALA BITCELL(a,b) |= BITMASK(a,b);(両方のテイクa引数としては、サイズを決定するために、後者は評価することはないaので、にのみ表示されますsizeof)。
R .. GitHub STOP HELPING ICE

1
@R ..この答えは本当に古いですが、この場合はマクロよりも関数のほうを好むでしょう。
PC Luddite、2015年

マイナー:第三(size_t)のキャストは一部だけを確実にするために存在しているようだ符号なしの数学をして%(unsigned)そこにできた。
chux-モニカを2017年

(size_t)(b)/(8*sizeof *(a))不必要に狭めることができb、分割前。非常に大きなビット配列に関する問題のみ。まだ面白いマクロです。
chux-モニカの復活2017年

25

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

しかし、オシロスコープベースのデバッグの最中に、これらの方法は、値がマイクロのPORTnSET / PORTnCLEARレジスターに直接書き込むのと比べて、CPUサイクルでかなりのオーバーヘッドがあり、タイトなループがある場所/高い場所で実際の違いがあることに驚いていました周波数ISRのトグルピン。

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


Microは、CodewarriorでCを使用するColdfire MCF52259でした。逆アセンブラー/ asmを見ると、CPUが最も基本的な操作を実行するために実行する必要があるすべての手順がわかるので便利です。<br>タイムクリティカルなループで他のCPUを消費する命令も見つけました-var%= max_valを実行して変数を制約すると、毎回CPUサイクルの山がかかりますが、if(var> max_val)var- = max_valはいくつかの指示。<BR>さらにいくつかのトリックに良いガイドはこちらです:codeproject.com/Articles/6154/...
ジョン・U

さらに重要なことに、ヘルパーメモリマップI / Oレジスタは、アトミック更新のメカニズムを提供します。シーケンスが中断されると、読み取り/変更/書き込みが非常にうまくいかない場合があります。
Ben Voigt

1
すべてのポートレジスタはとして定義されるvolatileため、コンパイラはそのようなレジスタを含むコードに対して最適化を実行できないことに注意してください。したがって、そのようなコードを逆アセンブルし、アセンブラレベルでどのように機能するかを確認することをお勧めします。
ランディン

24

ビットフィールドアプローチには、組み込み分野で他の利点があります。特定のハードウェアレジスタのビットに直接マップする構造体を定義できます。

struct HwRegister {
    unsigned int errorFlag:1;  // one-bit flag field
    unsigned int Mode:3;       // three-bit mode field
    unsigned int StatusCode:4;  // four-bit status code
};

struct HwRegister CR3342_AReg;

ビットパッキング順序に注意する必要があります。これはMSBが最初だと思いますが、これは実装に依存する場合があります。また、コンパイラーがバイト境界を越えるフィールドをどのように処理するかを確認します。

その後、以前と同様に、個々の値の読み取り、書き込み、テストを行うことができます。


2
ビットフィールドに関するほとんどすべては実装定義です。特定のコンパイラーがそれらをどのように実装するかに関するすべての詳細を見つけることができたとしても、コードでそれらを使用すると、間違いなく移植不可能になります。
ランディン2011

1
@Lundin-確かに、組み込みシステムのビット操作(特に、ハードウェアレジスタにあり、これが私の答えです)は、とにかく便利に移植できません。
Roddy

1
まったく異なるCPU間ではないでしょう。ただし、コンパイラ間および異なるプロジェクト間での移植性が必要な場合がほとんどです。また、データプロトコルのエンコード/デコードなど、ハードウェアにまったく関係のない組み込みの「ビット操作」がたくさんあります。
ランディン2011

...そして、組み込みプログラミングを行うビットフィールドを使用する習慣に慣れると、X86コードの実行速度が速くなり、無駄がなくなります。マシン全体でベンチマークを打ち破る単純なベンチマークではなく、プログラムがリソースを奪い合う実際のマルチタスク環境で。Advantage CISC-その本来の設計目標は、バスよりも高速なCPUと遅いメモリを補うことでした。

20

任意の型の変数の任意の場所でビットをチェックします。

#define bit_test(x, y)  ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )

使用例:

int main(void)
{
    unsigned char arr[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF };

    for (int ix = 0; ix < 64; ++ix)
        printf("bit %d is %d\n", ix, bit_test(arr, ix));

    return 0;
}

注: これは、高速で(柔軟性を考慮して)分岐しないように設計されています。Sun Studio 8をコンパイルすると、効率的なSPARCマシンコードが生成されます。また、amd64でMSVC ++ 2008を使用してテストしました。ビットを設定およびクリアするための同様のマクロを作成することが可能です。このソリューションと他の多くのソリューションとの主な違いは、ほとんどすべてのタイプの変数の任意の場所で機能することです。


20

より一般的には、任意のサイズのビットマップの場合:

#define BITS 8
#define BIT_SET(  p, n) (p[(n)/BITS] |=  (0x80>>((n)%BITS)))
#define BIT_CLEAR(p, n) (p[(n)/BITS] &= ~(0x80>>((n)%BITS)))
#define BIT_ISSET(p, n) (p[(n)/BITS] &   (0x80>>((n)%BITS)))

2
CHAR_BITは既にで定義されているためlimits.h、独自に組み込む必要はありませんBITS(実際にそうすることでコードが悪化します)
MM

14

このプログラムは、データビットを0から1または1から0に変更します。

{
    unsigned int data = 0x000000F0;
    int bitpos = 4;
    int bitvalue = 1;
    unsigned int bit = data;
    bit = (bit>>bitpos)&0x00000001;
    int invbitvalue = 0x00000001&(~bitvalue);
    printf("%x\n",bit);

    if (bitvalue == 0)
    {
        if (bit == 0)
            printf("%x\n", data);
        else
        {
             data = (data^(invbitvalue<<bitpos));
             printf("%x\n", data);
        }
    }
    else
    {
        if (bit == 1)
            printf("elseif %x\n", data);
        else
        {
            data = (data|(bitvalue<<bitpos));
            printf("else %x\n", data);
        }
    }
}

14

少しいじくり回している場合は、全体をより速くするマスクを使用することをお勧めします。次の関数は非常に高速で、柔軟性があります(任意のサイズのビットマップでビットをいじることができます)。

const unsigned char TQuickByteMask[8] =
{
   0x01, 0x02, 0x04, 0x08,
   0x10, 0x20, 0x40, 0x80,
};


/** Set bit in any sized bit mask.
 *
 * @return    none
 *
 * @param     bit    - Bit number.
 * @param     bitmap - Pointer to bitmap.
 */
void TSetBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] |= TQuickByteMask[n];        // Set bit.
}


/** Reset bit in any sized mask.
 *
 * @return  None
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
void TResetBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] &= (~TQuickByteMask[n]);    // Reset bit.
}


/** Toggle bit in any sized bit mask.
 *
 * @return   none
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
void TToggleBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] ^= TQuickByteMask[n];        // Toggle bit.
}


/** Checks specified bit.
 *
 * @return  1 if bit set else 0.
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
short TIsBitSet( short bit, const unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;    // Index to byte.
    n = bit % 8;    // Specific bit in byte.

    // Test bit (logigal AND).
    if (bitmap[x] & TQuickByteMask[n])
        return 1;

    return 0;
}


/** Checks specified bit.
 *
 * @return  1 if bit reset else 0.
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
short TIsBitReset( short bit, const unsigned char *bitmap)
{
    return TIsBitSet(bit, bitmap) ^ 1;
}


/** Count number of bits set in a bitmap.
 *
 * @return   Number of bits set.
 *
 * @param    bitmap - Pointer to bitmap.
 * @param    size   - Bitmap size (in bits).
 *
 * @note    Not very efficient in terms of execution speed. If you are doing
 *        some computationally intense stuff you may need a more complex
 *        implementation which would be faster (especially for big bitmaps).
 *        See (http://graphics.stanford.edu/~seander/bithacks.html).
 */
int TCountBits( const unsigned char *bitmap, int size)
{
    int i, count = 0;

    for (i=0; i<size; i++)
        if (TIsBitSet(i, bitmap))
            count++;

    return count;
}

16ビット整数のビット 'n'を設定するには、次のようにします。

TSetBit( n, &my_int);

ビット番号が渡したビットマップの範囲内にあることを確認するのはあなた次第です。バイト、ワード、dword、qwordなどがリトルエンディアンプロセッサの場合、メモリ内で相互に正しくマッピングされることに注意してください(リトルエンディアンプロセッサがビッグエンディアンプロセッサよりも「優れている」主な理由は、ああ、私は炎の戦争がやって来るのを感じますオン...)。


2
単一の演算子で実装できる関数にはテーブルを使用しないでください。TQuickByteMask [n]は(1 << n)と同等です。また、引数を短くすることは非常に悪い考えです。/および%は実際には除算であり、ビットシフト/ビット単位ではありません。2の累乗による符号付き除算はビット単位で実装できないためです。引数の型をunsigned intにしてください!
R .. GitHub ICE HELPING ICEの停止

これのポイントは何ですか?それはコードを遅くして読みにくくするだけですか?私はそれで一つの利点を見ることができません。1u << nは、Cプログラマにとって読みやすく、うまくいけば、単一のクロックティックCPU命令に変換できます。一方、あなたの除算は、特定のアーキテクチャが除算をどれだけうまく処理していないかに応じて、10ティック前後、または最大で100ティックまで変換されます。ビットマップ機能については、速度を最適化するために、各ビットインデックスをバイトインデックスに変換するルックアップテーブルを使用する方が理にかなっています。
ランディン2011

2
ビッグエンディアン/リトルエンディアンについては、ビッグエンディアンは整数と生データ(文字列など)を同じ方法でマッピングします:ビットマップ全体で左から右へのmsbからlsbへのマッピング。リトルエンディアンは整数を左から右に7-0、15-8、23-18、31-24としてマッピングしますが、生データは依然として左から右へのmsbからlsbです。したがって、リトルエンディアンが特定のアルゴリズムにとってどの程度優れているかは、私を完全に超えています。逆のようです。
ランディン2011

2
@R ..古いマイクロチップのmcuのようにプラットフォームが効率的にシフトできない場合、表は役立ちますが、もちろん、サンプルの分割はまったく非効率的です
jeb

12

これを使って:

int ToggleNthBit ( unsigned char n, int num )
{
    if(num & (1 << n))
        num &= ~(1 << n);
    else
        num |= (1 << n);

    return num;
}

5
まあ、それは非効率的な分岐を使用しています。
asdf

3
@asdfコンパイラの仕事は最も効率的なバイナリを出力することであり、プログラマの仕事は明確なコードを書くことです
MM

3
これは、特定のビットのテスト、設定、およびクリアの優れたデモンストレーションです。しかし、それは少しトグルするための非常に悪いアプローチです。
Ben Voigt

10

bitset答えを拡張する:

#include <iostream>
#include <bitset>
#include <string>

using namespace std;
int main() {
  bitset<8> byte(std::string("10010011");

  // Set Bit
  byte.set(3); // 10010111

  // Clear Bit
  byte.reset(2); // 10010101

  // Toggle Bit
  byte.flip(7); // 00010101

  cout << byte << endl;

  return 0;
}

10

LinuxカーネルでCプログラミングを使用してこのすべての操作を実行する場合は、Linuxカーネルの標準APIを使用することをお勧めします。

https://www.kernel.org/doc/htmldocs/kernel-api/ch02s03.htmlを参照してください

set_bit  Atomically set a bit in memory
clear_bit  Clears a bit in memory
change_bit  Toggle a bit in memory
test_and_set_bit  Set a bit and return its old value
test_and_clear_bit  Clear a bit and return its old value
test_and_change_bit  Change a bit and return its old value
test_bit  Determine whether a bit is set

注:ここでは、操作全体が1つのステップで行われます。したがって、これらはすべてSMPコンピューターでもアトミックであることが保証されており、プロセッサー間で一貫性を保つのに役立ちます。


9

Visual C 2010、およびおそらく他の多くのコンパイラーは、組み込みのブール演算を直接サポートしています。ビットにはブール値と同じように2つの値があり、ブール値を使用できます。この表現のメモリ。これは機能しsizeof()ますが、オペレーターでも正しく機能します。

bool    IsGph[256], IsNotGph[256];

//  Initialize boolean array to detect printable characters
for(i=0; i<sizeof(IsGph); i++)  {
    IsGph[i] = isgraph((unsigned char)i);
}

だから、あなたの質問にIsGph[i] =1、またはIsGph[i] =0、ブールの設定とクリアを簡単にしてください。

印刷できない文字を見つけるには:

//  Initialize boolean array to detect UN-printable characters, 
//  then call function to toggle required bits true, while initializing a 2nd
//  boolean array as the complement of the 1st.
for(i=0; i<sizeof(IsGph); i++)  {
    if(IsGph[i])    {
         IsNotGph[i] = 0;
    }   else   {
         IsNotGph[i] = 1;
    }
}

このコードには「特別な」ものは何もないことに注意してください。それは少し整数のように扱います-技術的にはそうです。2つの値と2つの値のみを保持できる1ビット整数。

私はかつてこのアプローチを使用して、6桁のローン番号をビット配列へのインデックスとして使用し、loan_numberがISAMキーである重複したローンレコードを見つけました。非常に速く、8か月後、データの取得元であるメインフレームシステムが実際に誤動作していることが証明されました。ビット配列の単純さにより、たとえば検索アプローチと比較して、ビット配列の正確さが非常に高くなります。


std :: bitsetは、実際にはほとんどのコンパイラーによってビットとして実装されています
ガリネット2014年

@galinette、同意した。ヘッダーファイル#include <bitset>は、この点で優れたリソースです。また、ベクターのサイズを変更する必要がある場合のための特別なクラスvector <bool>。C ++ STL、第2版、Nicolai M. Josuttisが、それぞれ650ページと281ページで網羅しています。C ++ 11はstd :: bitsetにいくつかの新しい機能を追加します。特に興味深いのは、順序付けされていないコンテナー内のハッシュ関数です。ヘッドアップをありがとう!頭がおかしいコメントを削除します。すでに十分なゴミがウェブ上にあります。追加したくありません。

3
これは、それぞれに少なくとも1バイトのストレージを使用しますboolint実装に使用するC89セットアップでは、たぶん4バイトbool
MM

@MattMcNabb、あなたは正しいです。C ++では、ブール値を実装するために必要なint型のサイズは規格で指定されていません。私はこの答えが少し前に間違っていたことに気づきましたが、人々がそれが有用であると明らかにしているので、ここに残すことにしました。ここに私のビットのライブラリ...であるとして使用するビットgalinetteさんのコメントに望む人のために最も有用であるstackoverflow.com/a/16534995/1899861

2
@RocketRoy:おそらく、これは「ビット操作」の例であると主張する文を変更する価値があります。
Ben Voigt、

6

ここで定義されている演算子のいずれかを使用します

ビットを設定するint x = x | 0x?;?は、ビット位置をバイナリ形式で使用します。


2
0xバイナリではなく、16進数のリテラルの接頭辞です。
Ben Voigt、

5

ここに私が使用するいくつかのマクロがあります:

SET_FLAG(Status, Flag)            ((Status) |= (Flag))
CLEAR_FLAG(Status, Flag)          ((Status) &= ~(Flag))
INVALID_FLAGS(ulFlags, ulAllowed) ((ulFlags) & ~(ulAllowed))
TEST_FLAGS(t,ulMask, ulBit)       (((t)&(ulMask)) == (ulBit))
IS_FLAG_SET(t,ulMask)             TEST_FLAGS(t,ulMask,ulMask)
IS_FLAG_CLEAR(t,ulMask)           TEST_FLAGS(t,ulMask,0)

5

使用される変数

int value, pos;

値-データの
位置-設定、クリア、またはトグルするビットの位置。

ビットを設定します。

value = value | 1 << pos;

少しクリア:

value = value & ~(1 << pos); 

少し切り替えます:

value = value ^ 1 << pos;

5
int set_nth_bit(int num, int n){    
    return (num | 1 << n);
}

int clear_nth_bit(int num, int n){    
    return (num & ~( 1 << n));
}

int toggle_nth_bit(int num, int n){    
    return num ^ (1 << n);
}

int check_nth_bit(int num, int n){    
    return num & (1 << n);
}

戻り値の型check_nth_bitもできますbool
Xeverous

@Xeverousはい、それは発信者の意図に依存します
Sazzad Hissain Khan

5

最初に
num = 55整数でビット単位の操作(設定、取得、クリア、トグル)を実行するとします。
n = 4ビット単位の演算を実行する0ベースのビット位置。

少し入手するには?

  1. nthnumビットの右シフトを取得するにはnumn時間。次に&、1とビットごとのAND を実行します。
bit = (num >> n) & 1;

使い方?

       0011 0111 (55 in decimal)
    >>         4 (right shift 4 times)
-----------------
       0000 0011
     & 0000 0001 (1 in decimal)
-----------------
    => 0000 0001 (final result)

少しセットするには?

  1. 数値の特定のビットを設定します。左シフト1 n回。次に、ビットごとのOR |演算をで実行しnumます。
num |= (1 << n);    // Equivalent to; num = (1 << n) | num;

使い方?

       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
       0001 0000
     | 0011 0111 (55 in decimal)
-----------------
    => 0001 0000 (final result)

少しクリアするには?

  1. 左シフト1、nつまり1 << n
  2. 上記の結果でビット単位の補数を実行します。したがって、n番目のビットは設定されず、残りのビットは設定されます。~ (1 << n)ます。
  3. 最後に、&上記の結果とビット単位のAND 演算を実行しnumます。上記の3つのステップをまとめると、次のようになりnum & (~ (1 << n))ます。

ビットをクリアする手順

num &= (~(1 << n));    // Equivalent to; num = num & (~(1 << n));

使い方?

       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
     ~ 0001 0000
-----------------
       1110 1111
     & 0011 0111 (55 in decimal)
-----------------
    => 0010 0111 (final result)

少し切り替える方法は?

ビットを切り替えるには、ビットごとのXOR ^演算子を使用します。ビットごとのXOR演算子は、両方のオペランドの対応するビットが異なる場合は1と評価され、それ以外の場合は0と評価されます。

つまり、ビットをトグルするということです。トグルするビットと1でXOR演算を実行する必要があります。

num ^= (1 << n);    // Equivalent to; num = num ^ (1 << n);

使い方?

  • トグルのビットは、その後、0の場合0 ^ 1 => 1
  • トグルのビットは、その後、1である場合1 ^ 1 => 0
       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
       0001 0000
     ^ 0011 0111 (55 in decimal)
-----------------
    => 0010 0111 (final result)

推奨読書- ビットごとのオペレーター演習


詳しい説明ありがとうございます。BITマジックリンクの
チャンドラシェ

4

シングルビットをどのように設定、クリア、トグルしますか?

マスクを作成しようとするときに一般的なコーディングの落とし穴に対処するに
1は、必ずしも十分な幅があるとは限りません

numberより広いタイプの場合、どのような問題が発生し1ますか?未定義の動作(UB)につながる
xシフトには大きすぎるかもしれません。それほど大きくなくても、最上位ビットが十分にフリップされない場合があります。1 << xx~

// assume 32 bit int/unsigned
unsigned long long number = foo();

unsigned x = 40; 
number |= (1 << x);  // UB
number ^= (1 << x);  // UB
number &= ~(1 << x); // UB

x = 10;
number &= ~(1 << x); // Wrong mask, not wide enough

1の幅が十分であることを保証するには:

コードを使用し1ullたり、コードを変更したり(uintmax_t)1して、コンパイラーを最適化できます。

number |= (1ull << x);
number |= ((uintmax_t)1 << x);

またはキャスト-キャストを正しく最新に保つためにコーディング/レビュー/メンテナンスの問題を引き起こします。

number |= (type_of_number)1 << x;

または優しく促進1の種類として少なくとも同じ幅である数学の操作を強制的にnumber

number |= (number*0 + 1) << x;

仕事に最善の最もビット操作と同様に、符号なしのタイプではなく、署名したもの


古い質問を見て面白い!符号付きの型の符号ビットを設定するnumber |= (type_of_number)1 << x;こともnumber |= (number*0 + 1) << x;適切ではありません...実際、どちらもそうではありませんnumber |= (1ull << x);。位置によってそれを行うポータブルな方法はありますか?
chqrlie 2017

1
@chqrlie IMO、符号ビットの設定およびシフトによるUBまたはIDBのリスクを回避する最良の方法は、符号なしの型を使用することです。移植性の高いシフト署名付きコードは複雑すぎて受け入れられません。
chux-モニカを2017年

3

C ++ 11テンプレートバージョン(ヘッダーに配置):

namespace bit {
    template <typename T1, typename T2> inline void set  (T1 &variable, T2 bit) {variable |=  ((T1)1 << bit);}
    template <typename T1, typename T2> inline void clear(T1 &variable, T2 bit) {variable &= ~((T1)1 << bit);}
    template <typename T1, typename T2> inline void flip (T1 &variable, T2 bit) {variable ^=  ((T1)1 << bit);}
    template <typename T1, typename T2> inline bool test (T1 &variable, T2 bit) {return variable & ((T1)1 << bit);}
}

namespace bitmask {
    template <typename T1, typename T2> inline void set  (T1 &variable, T2 bits) {variable |= bits;}
    template <typename T1, typename T2> inline void clear(T1 &variable, T2 bits) {variable &= ~bits;}
    template <typename T1, typename T2> inline void flip (T1 &variable, T2 bits) {variable ^= bits;}
    template <typename T1, typename T2> inline bool test_all(T1 &variable, T2 bits) {return ((variable & bits) == bits);}
    template <typename T1, typename T2> inline bool test_any(T1 &variable, T2 bits) {return variable & bits;}
}

このコードは壊れています。(また、なぜ;関数定義の後にあるのですか?)
melpomene '10

@melpomeneコードは壊れていません。テストしました。それがコンパイルされない、または結果が間違っているということですか?追加の「;」について 覚えていません。これらは実際に削除できます。
Joakim L. Christiansen

(variable & bits == bits)
メルポメン

気づいていただきありがとうございます、それは((variable & bits) == bits)
ジョアキム・L・クリスチャンセン

std::bitsetc ++ 11での使用
pqnet

0

このプログラムは、@ Jeremyの上記のソリューションに基づいています。誰かがすぐに遊びたいなら。

public class BitwiseOperations {

    public static void main(String args[]) {

        setABit(0, 4); // set the 4th bit, 0000 -> 1000 [8]
        clearABit(16, 5); // clear the 5th bit, 10000 -> 00000 [0]
        toggleABit(8, 4); // toggle the 4th bit, 1000 -> 0000 [0]
        checkABit(8,4); // check the 4th bit 1000 -> true 
    }

    public static void setABit(int input, int n) {
        input = input | ( 1 << n-1);
        System.out.println(input);
    }


    public static void clearABit(int input, int n) {
        input = input & ~(1 << n-1);
        System.out.println(input);
    }

    public static void toggleABit(int input, int n) {
        input = input ^ (1 << n-1);
        System.out.println(input);
    }

    public static void checkABit(int input, int n) {
        boolean isSet = ((input >> n-1) & 1) == 1; 
        System.out.println(isSet);
    }
}


Output :
8
0
0
true

-2

C言語で次の関数のいずれかを試して、nビットを変更します。

char bitfield;

// Start at 0th position

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}

または

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}

または

void chang_n_bit(int n, int value)
{
    if(value)
        bitfield |= 1 << n;
    else
        bitfield &= ~0 ^ (1 << n);
}

char get_n_bit(int n)
{
    return (bitfield & (1 << n)) ? 1 : 0;
}

value << n未定義の動作を引き起こす可能性があります
MM
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.