ビット演算によるフラグ操作
8ビット表示の値でREAD・WRITE・EXECを操作します。追加、解除、反転、複数条件の検査を行い、未定義のビットが含まれる入力も確認します。
説明
ビットフラグは整数の各ビットに独立した状態を割り当てる表現です。複数の状態を一つの値に含め、マスクで必要な位置だけを調べたり変更したりできます。
8ビット表示の値でREAD・WRITE・EXECを操作します。追加、解除、反転、複数条件の検査を行い、未定義のビットが含まれる入力も確認します。
1各ビットの意味
最下位からREAD=1、WRITE=2、EXEC=4を割り当てます。残りのビットはこの形式では未定義です。
READ = 1; WRITE = 2; EXEC = 4独立した位置のマスク; flags = READ | EXEC二つの状態を合わせる; print("name binary")対応表の見出し; print("READ ", format(READ, "08b")); print("WRITE", format(WRITE, "08b")); print("EXEC ", format(EXEC, "08b")); print("flags", format(flags, "08b"))マスクと現在値を8桁で表示ORの|でREADとEXECを合わせると00000101です。ここでの8桁は表示幅であり、Pythonの整数が8ビットに制限されたわけではありません。
2マスクで必要な位置を取り出す
各フラグとのANDを計算します。マスクに対応するビット以外は0になります。
print(flags & READ, flags & WRITE, flags & EXEC各位置を抽出); print(bool(flags & READ), bool(flags & WRITE), bool(flags & EXEC)0かどうかで有効性を判定)ANDの結果は1・0・4で、boolへ変換するとTrue・False・Trueです。EXECが有効でも結果は1ではなく4なので、常に== 1で調べると誤判定します。
3同じフラグを二度追加する
WRITEをORで追加し、もう一度同じ操作を行います。
flags |= WRITEWRITEを1にする; print(format(flags, "08b"))追加後; flags |= WRITE同じ追加を繰り返す; print(format(flags, "08b"))二度目の結果どちらも00000111です。ORで立てる操作は、既に1のビットを0へ戻しません。同じ追加要求が繰り返されても、この値は変わりません。
4他のビットを保って解除する
WRITEを反転したマスクとのANDで、その位置だけを0にします。
flags &= ~WRITEWRITEだけを0にする; print(format(flags, "08b"))解除後; print(~WRITE, format((~WRITE) & 0xff, "08b"))反転値と下位8ビットflagsは00000101へ戻ります。Pythonの~2は-3ですが、下位8ビットだけを見ると11111101です。解除する位置だけ0、他の位置は1なのでREADとEXECを保てます。
5反転は追加とは違う
XORの^でWRITEを反転し、さらにもう一度反転します。
flags ^= WRITEWRITEの状態を反転; print(format(flags, "08b"))一度目; flags ^= WRITE再度反転; print(format(flags, "08b"))二度目00000111から00000101へ戻ります。XORは0なら1、1なら0にするため、同じ操作の繰返しで状態が変わります。「有効にする」要求にはOR、「切り替える」要求にはXORと使い分けます。
6どれか有効か、全て有効か
READとWRITEの両方が必要な処理を考えます。現在はREADだけが含まれるため、単なる真偽では判定が足りません。
required = READ | WRITE必要な二つのマスク; print(bool(flags & required)一つ以上が有効, (flags & required) == required全ての必要ビットが有効)どれか一つでも含めばTrueですが、必要なビット全てを含む比較はFalseです。複数フラグの判定では、一つ以上でよいのか全て必要なのかを明示します。
7未定義のビットを検出する
外部から00001001を受け取ったとします。定義済みの下位3ビット以外が立っているか確認します。
allowed = READ | WRITE | EXEC定義済み位置; incoming = 0b00001001受信した値; unknown = incoming & ~allowed未定義位置を抽出; print(format(incoming, "08b"), format(unknown, "08b"))元の値と未知ビット; print(0 <= incoming <= 255 and unknown == 0)幅と意味の両方を検査未定義の00001000が検出され、受入れ条件はFalseです。8桁表示だけでは範囲検査にならないため、0〜255と未定義ビットなしの両方を確認しています。権限の実装では、ビットの検査に加えて利用者認証なども必要です。
まとめ
ORで追加、ANDと反転マスクで解除、XORで切替を行います。
複数ビットの必要条件と未定義ビットの扱いを明示します。
操作履歴と解説を振り返れます。
Python 3
READ = 1; WRITE = 2; EXEC = 4; flags = READ | EXEC; print("name binary"); print("READ ", format(READ, "08b")); print("WRITE", format(WRITE, "08b")); print("EXEC ", format(EXEC, "08b")); print("flags", format(flags, "08b"))name binary READ 00000001 WRITE 00000010 EXEC 00000100 flags 00000101
print(flags & READ, flags & WRITE, flags & EXEC); print(bool(flags & READ), bool(flags & WRITE), bool(flags & EXEC))1 0 4 True False True
flags |= WRITE; print(format(flags, "08b")); flags |= WRITE; print(format(flags, "08b"))00000111 00000111
flags &= ~WRITE; print(format(flags, "08b")); print(~WRITE, format((~WRITE) & 0xff, "08b"))00000101 -3 11111101
flags ^= WRITE; print(format(flags, "08b")); flags ^= WRITE; print(format(flags, "08b"))00000111 00000101
required = READ | WRITE; print(bool(flags & required), (flags & required) == required)True False
allowed = READ | WRITE | EXEC; incoming = 0b00001001; unknown = incoming & ~allowed; print(format(incoming, "08b"), format(unknown, "08b")); print(0 <= incoming <= 255 and unknown == 0)00001001 00001000 False
演習完了