閉じる

ビット演算子の使い方入門:フラグ管理を基礎から解説

ビット演算子は複数の真偽状態を1つの整数で効率よく扱える道具です。

初めてでも安心して読めるように、ビットと2進数の基本から、フラグ(ビットマスク)の設定・解除・確認の実例まで、順序立てて解説します。

小さなサンプルコードで動きを確かめながら、基礎を確実に身につけましょう

ビット演算子の基礎

ビットと2進数の基本

なぜビットで考えるのか

コンピュータは0と1の並び(ビット)で情報を表現します。

複数のオン・オフ状態をひとまとめに持ち運ぶには、ビット単位の表現が最もコンパクトで高速です

例えば4つの状態を4個の真偽値で持つのではなく、1つの整数(4ビット)にまとめられます。

2進数の見方

10進数の13は、2進数では1101です。

左から順に8、4、2、1の重みを持ち、1101は8+4+0+1=13を意味します。

各ビットが独立したスイッチのようにオン(1)かオフ(0)を示す、と理解すると扱いやすくなります

例(8ビットの表示)

8ビットの値0101 0011は、上位から「64+16+2+1=83」を表します。

この「各ビットが独立」という性質を利用して、1つの整数の中で複数のフラグを同時に管理できます

ビット演算子の種類

主な演算子一覧

ビット演算子には、ビット同士を合成したり、反転したり、位置をずらすためのものがあります。

最低限、AND・OR・XOR・NOT・左シフト・右シフトの6種を押さえれば実用に足ります

以下はJavaScriptでの例ですが、CやJavaなどでも同様です。

演算子名前役割例と結果(2進数)
&AND両方が1なら11101 & 1011 = 1001
|ORどちらかが1なら11101 | 1011 = 1111
^XOR異なると11101 ^ 1011 = 0110
~NOT反転~0000 1111 = 1111 0000
<<左シフト左にずらす(×2のn乗)0001 << 3 = 1000
>>右シフト右にずらす(÷2のn乗)1000 >> 3 = 0001

ANDは選び出し、ORは足し合わせ、XORは切り替え、NOTは反転、シフトは桁移動と覚えておくと実務で迷いません

初心者が混同しやすい記号

論理演算の&&や||と、ビット演算の&や|は別物です。

ビット単位の合成には&や|を、真偽値どうしの条件評価には&&や||を使います

真偽値との違い

何が違うのか

真偽値はtrueかfalseの1つだけを持ちますが、ビット値は複数ビットを一度に持てます。

つまりビットは「複数の真偽値を圧縮して持つ」表現で、1つの整数に多数の状態を格納できます

例で比較

  • 論理演算: isReady && isVisible
  • ビット演算: (flags & VISIBLE) != 0

前者は2つのbooleanを評価、後者は整数flagsの中からVISIBLEビットが立っているかを取り出して確認しています

使い分けの目安

保持したい状態が3つ以上になったら、フラグ(ビット)でまとめるとコードがすっきりします。

配列やオブジェクトにbooleanを並べるより、ビットで持つ方が比較や保存が軽量です

フラグ管理の基本とビットマスク

フラグとは

定義

フラグとは、ある条件が有効か無効かを示す1ビットの印です。

1つの整数の各ビットを別々のフラグとして割り当て、複数の状態を同時に表現します

簡単なイメージ

「見える」「書ける」「実行できる」「隠す」などの状態を、それぞれ1ビットで管理します。

1ならオン、0ならオフという単純なルールなので、人にも機械にも扱いやすいのが特徴です

ビットマスクの定義

マスクとは

ビットマスクは、関心のあるビット位置を1にした値です。

AND(&)で「必要なビットだけを取り出すフィルター」として機能します

例(JS)

JavaScriptで定数を定義します。

JavaScript
// それぞれ固有のビット(2の累乗)を割り当てる
const READ   = 1 << 0; // 0001
const WRITE  = 1 << 1; // 0010
const EXEC   = 1 << 2; // 0100
const HIDDEN = 1 << 3; // 1000

各フラグは必ず「2の累乗(1,2,4,8,…)」にし、ビット位置が被らないようにします

フラグを設定する

ORで立てる

あるフラグをオンにするにはOR(|)を使います。

JavaScript
let flags = 0;
flags |= READ;      // READをオン
flags |= WRITE;     // WRITEをオン
// flagsは 0000 -> 0001 -> 0011

ORは「足し合わせる」イメージで、既存のビットを壊さずに新しいビットを追加できます

フラグを解除する

ANDとNOTで落とす

オフにするときはAND(&)とNOT(~)を組み合わせます。

JavaScript
flags &= ~WRITE;    // WRITEをオフにする
// ~WRITE は 1101、ANDで該当ビットだけ0に

解除は「消したいビット位置だけ0、他は1のマスク」を作ってANDする、と覚えましょう

フラグを切り替える

XORでトグル

オンならオフに、オフならオンにする切り替えにはXOR(^)が便利です。

JavaScript
flags ^= HIDDEN;    // HIDDENをトグル

XORは「異なっていれば1」なので、同じ操作をもう一度行うと元に戻る特性があります

フラグを確認する

ANDでチェック

フラグが立っているかは、AND(&)の結果が0かどうかで判断します。

JavaScript
if ((flags & READ) != 0) {
  console.log("読めます");
}

「等しいか」ではなく「0でないか」を見るのがポイントです

初期化とデフォルト

すべてオフや複数オンで開始

初期値0は全フラグオフを意味します。

複数を既定でオンにしたい場合はORでまとめます。

JavaScript
let flags = READ | WRITE; // 既定で読み書き可

初期化時に「何がオンか」を定数で明示しておくと、後から仕様が変わっても修正が安全です

実践パターン

複数フラグの同時設定

複数をまとめて立てる

一度に複数のフラグをオンにするにはORで合成します。

JavaScript
flags |= READ | EXEC;  // READとEXECを同時にオン

「合成してから一度に適用」の形は、分岐や関数引数にも流用しやすく、意図が読みやすくなります

条件分岐でのフラグチェック

ANDの結果を明確に比較

分岐で使う時は括弧を付けて可読性を高めます。

JavaScript
if ((flags & (READ | WRITE)) != 0) {
  console.log("読み書きのどちらかが可能");
}

if ((flags & (READ | WRITE)) === (READ | WRITE)) {
  console.log("読み書きの両方が可能");
}

「いずれかが立っている」か「全てが立っている」かで比較方法が変わる点を押さえましょう

権限や状態の管理に使う例

権限フラグのサンプル

実用では、権限やUI状態、ゲームの状態などをまとめて持ちます。

例として権限を表にします。

フラグ名値(10進)ビット(2進)意味
READ10001読み取り
WRITE20010書き込み
EXEC40100実行
HIDDEN81000非表示

このように一覧化しておくと、レビューやデバッグ時に「どのビットが何を意味するか」が即座に共有できます

実コード例

JavaScript
function canRead(flags)  { return (flags & READ)  != 0; }
function canWrite(flags) { return (flags & WRITE) != 0; }

const userFlags = READ | WRITE;
if (canRead(userFlags) && canWrite(userFlags)) {
  console.log("読み書き可能");
}

チェック処理を関数化すると、式の重複を減らしバグの混入を防げます

enumや定数で読みやすくする

定数オブジェクト(JS)

JavaScriptでは定数名で意味を表すのが基本です。

JavaScript
const Perm = Object.freeze({
  READ:   1 << 0,
  WRITE:  1 << 1,
  EXEC:   1 << 2,
  HIDDEN: 1 << 3,
});

// 使用例
let flags = Perm.READ | Perm.WRITE;

名前付き定数にするだけで「何のビットか」が一目で分かり、マジックナンバーを排除できます

補足(PythonのIntFlag)

Pythonならenum.IntFlagが使えます。

Python
from enum import IntFlag, auto

class Perm(IntFlag):
    READ   = auto()  # 1
    WRITE  = auto()  # 2
    EXEC   = auto()  # 4
    HIDDEN = auto()  # 8

flags = Perm.READ | Perm.WRITE
if flags & Perm.READ:
    print("読めます")

言語に応じてenumや属性を活用すると、型安全性と自己文書性が高まります

シフト演算でフラグを増やす

ビット位置をずらして定義

増えるたびに値を数える必要はありません。

1を左にシフトすれば新しいビットが作れます。

JavaScript
const FLAG0 = 1 << 0;
const FLAG1 = 1 << 1;
const FLAG2 = 1 << 2;
// あるいはループで自動生成も可

「1を左にn回シフト」で2のn乗になるので、ビット位置の重複を自然と回避できます

よくあるミスと注意点

フラグ値の重複を避ける

2の累乗にする理由

例えばREAD=1、WRITE=2、EXEC=4のように、必ず2の累乗にします。

重複する値(例: READ=1、WRITE=1)や連番(1,2,3)は誤判定の原因になります

ダメな例

JavaScript
// 悪い例: WRITEが3だと 0011 でREADと被る
const READ = 1;   // 0001
const WRITE = 3;  // 0011  ←重複

WRITEが立っている時にREADも立っていると判定されてしまい、意図しない結果になります

優先順位は括弧で明確にする

比較よりANDを先に

演算子の優先順位を誤るとバグになります。

必ず「(flags & MASK) != 0」のように括弧でAND部分を明示しましょう

JavaScript
// 良い
if ((flags & READ) != 0) { /* ... */ }

// 悪い(読み手に不親切、言語差で誤動作の恐れ)
if (flags & READ != 0) { /* ... */ }

括弧は可読性のためだけでなく、評価順を固定して安全にする役割もあります

ビット幅と符号に注意

JavaScriptの32ビット制約

JavaScriptのビット演算は内部的に32ビット符号付き整数で行われます。

31個を超えるフラグ(最上位ビット含む)や大きな左シフトは負数になり得るため注意が必要です

JavaScript
// 31ビット目(1<<31)は負数に見える
console.log(1 << 31); // -2147483648

// 対策: 使うビット数を抑える、またはBigIntを使う
const A = 1n << 40n;  // BigInt同士ならOK

多数のフラグが必要ならBigIntを検討するか、用途別に複数のflags変数に分ける設計を選びましょう

==で比較しない((flags & X) != 0で確認)

等値比較が危険な理由

複数フラグが立つ可能性があるのに「flags == READ」のように書くと、READ以外のビットが立った瞬間に偽になります。

「そのビットが立っているか」を見るなら常に(flags & READ) != 0の形で確認します

JavaScript
const flags = READ | WRITE;

if (flags == READ) {
  // ここには来ない(WRITEも立っているため等しくない)
}

if ((flags & READ) != 0) {
  // ここに来る(READビットは立っている)
}

等値比較は「それしか立っていない」を意味しますが、通常は「それが含まれているか」を知りたいはずです

マジックナンバーを避ける

名前を付けて意味を共有

コード中に直接1や2や4を書くと意図が不明瞭です。

必ず定数やenum名で表し、定義を1か所に集約して保守しやすくします

JavaScript
// 悪い
if ((flags & 4) != 0) { /* 4って何? */ }

// 良い
if ((flags & EXEC) != 0) { /* 実行権限 */ }

定数名は仕様書の言葉と揃えると、開発者間の認識がずれにくくなります

まとめ

ビット演算子は、複数のオン・オフ状態を1つの整数で素早く扱う強力な道具です

基礎として、フラグは2の累乗で定義し、設定はOR、解除はANDとNOT、切り替えはXOR、確認は(flags & MASK) != 0という型を確実に覚えましょう。

論理演算(&&, ||)と混同せず、括弧で評価順を明確にすれば、読みやすく安全なコードになります。

最後に、マジックナンバーを避けて定数やenumを使い、ビット幅や符号の制約(JSは32ビット)に気をつけることで、初学者でも実務で使える堅牢なフラグ管理を実現できます。

この記事を書いた人
エーテリア編集部
エーテリア編集部

このサイトでは、プログラミングをこれから学びたい初心者の方に向けて記事を書いています。 基本的な用語や環境構築の手順から、実際に手を動かして学べるサンプルコードまで、わかりやすく整理することを心がけています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!