私はこのコード行にアクセスしました:
#define CPARSER_FLAGS_DEBUG (1 << 0)
それは何をするためのものか?それは次と同じです:
#define CPARSER_FLAGS_DEBUG (1)
右?
私はこのコード行にアクセスしました:
#define CPARSER_FLAGS_DEBUG (1 << 0)
それは何をするためのものか?それは次と同じです:
#define CPARSER_FLAGS_DEBUG (1)
右?
はい、そうです。フラグの値を設定するときに対称に使用される可能性があります。
#define FLAG_1 (1 << 0)
#define FLAG_2 (1 << 2)
#define FLAG_3 (1 << 3)
/* ... */
パフォーマンスについて心配する必要はありません。優れたコンパイラは、そのような操作を最適化できます。
これらの値を次のように組み合わせることができます。
/* Flags FLAG_1, FLAG_2 and FLAG_3 are set. */
f = FLAG_1 | FLAG_2 | FLAG_3;
そして、特定のフラグが設定されているかどうかをテストします。
/* True if FLAG_1 is set. */
if (f & FLAG_1) { /* ... */ }
C にインスパイアされた言語では<<
、>>
演算子は左右のビットごとのシフト演算子です (ただし、C++ ではオーバーロードできます — 最も有名なオーバーロードはおそらくI/O ストリーム演算子です)。例えば、
x = y << 2;
y を 2 ビット左にシフトした結果を x に代入します。
通常、低レベル コードでは多くのバイト シフトが見られますが、その理由は次のとおりです...どのハードウェアもその動作を構成および制御する方法を提供しますが、整数全体を使用して、たとえば ON/OFF 状態を表すことは誰も望んでいません。したがって、ハードウェア開発者は通常、単一の整数 (別名レジスタ、ほとんどの場合符号なし 32 ビット) を提供し、たとえば、ビット #0 はデータ送信を有効または無効にし、ビット #1 はデータ フィルタリングを有効または無効にし、ビット #3 はデータ フィルタリングを有効または無効にします。いくつかの他の魔法とそのような力。通常、1 つまたは複数の設定を同時に読み取ったり変更したりできます。これがソフトウェア開発者にとってどれほど便利か想像してみてください。単純な整数 (またはブール値) を扱う代わりに、プログラマーは通常 CPU がアドレス指定できないビットを処理する必要があります。生活を簡素化するために、開発者はマスクを定義します。上記の例に固執すると、
#define MYDEV_ENABLE_DATA_FLOW (1u<<0)
#define MYDEV_ENABLE_FILTERING (1u<<1)
#define MYDEV_ENABLE_MAGIC (1u<<2)
シフト式の右辺が既知であるため、コンパイラは各値に対して次の整数をそれぞれ生成します。
最初はあまり意味をなさないかもしれませんが、これらの値をバイナリ表現で見ると、次のようになります。
つまり、各値には、異なる位置に設定されたビットが 1 つだけあります。次に、データ転送と魔法の機能を有効にしたいが、架空のデバイスのフィルタリングを有効にしたくないとします。そのためには、ビット #0 と #2 を設定 ( 1
) し、ビット #1 を設定解除 ( 0
) する必要があります。これは、事前定義されたマスクと一緒にビット単位の OR演算子が役立つときです。私たちがしていることはこれです:
uint32_t value_for_device = MYDEV_ENABLE_DATA_FLOW | MYDEV_ENABLE_MAGIC;
値を与える"OR"0b001
と。これをデバイスに送信すると、すべてのビットがチェックされ、対応する機能が有効または無効になります。0b100
0b101
他のビット操作もよく使用されます。たとえば、現在有効になっているものと無効になっているものがわからず、何も変更したくなく、データ フィルタリングがオフになっていることを確認します。これは、現在の構成を読み取り、ビットの設定を解除し、書き戻すことで実行できます。例えば:
uint32_t value;
value = read_from_device();
value &= (~MYDEV_ENABLE_FILTERING);
write_to_device(value);
もちろん使い方はこれだけではありません。多くの有用な例については、Sean Eron Anderson による「Bit Twiddling Hacks」を参照してください。
OK、元の質問に戻ります — なぜ(1<<0)
単に ではなく書くの(1)
ですか? いくつかの理由があります。
次のようなものがあると、より一貫性があります。
#define A (1<<0)
#define B (1<<1)
#define C (1<<2)
これではなく:
#define A (1)
#define B (1<<1)
#define C (1<<2)
またはこれでも:
#define A 1
#define B 2
#define C 4
これにより、物事を簡単に変更できます。シフト幅を変更するだけで簡単に変更でき、追加/削除を繰り返す必要はありません。
コードを読む人に対して、作者の意図を明確に示します。あまり1
意味がありません。しかし、1<<0
ビット シフトが含まれていて、コードがビット マスクで動作していると考える可能性が最も高いでしょう。
シフトを実行する必要がある数がマクロとして定義されていると想像してください。例えば:
#define MY_BIT (1u<<MAGIC_BIT_OFFSET)
それでは、結果が正しいかどうかはわかり1
ません。また、ビット オフセットの定義を個別に保持できます。
これを行う理由が他にもあると思いますが、すぐには思い浮かびません。
それが少し物事を解決することを願っています。幸運を!
これは通常、定義がビット フラグを表していることを示すために行われます。特に。複数のフラグが一緒に定義されている場合。この場合、シフトの大きさによって、定義が表すビット位置が定義されます。そのように定義すると、物事がうまく整列します。
#define FOO_FLAG (1 << 0)
#define BAR_FLAG (1 << 1)
#define BAZ_FLAG (1 << 2)
これは、次のような一連のフラグを期待する関数を呼び出すときに使用できます。
int ret = some_function(FOO_FLAG | BAR_FLAG) & BAZ_FLAG;
0
次に、ビットと(FOO & BAR) を指定して関数を呼び出し、1
戻り値をチェックして、ビット2
(BAZ) が設定されているかどうかを確認します。