C Primer Plusのビット操作

4201 ワード

バイナリ符号表現
ほとんどのコンピュータは、nビット記憶部に位置する符号付き整数を格納するためにバイナリ符号化表現を使用する.
バイナリ符号表現法では,最左ビットが符号を決定する.0の場合、整数は正(すなわち負ではない)であり、1の場合、整数は負である.
2つの演算:
  • 逆符号(または整数の逆符号):各ビットを単純に反転する、すなわち0ビットを1ビットに、1ビットを0ビットに変更する.
  • 補符号(または整数の補符号をとる):方法1、まず、右からビットをコピーし、1がコピーされるまで;次に、残りのビットを反転する.方法2、まずそれに対して1回の逆符号演算を行い、1を加えて結果を得る.
  • 整数をバイナリ符号化形式で格納し、コンピュータは次の手順に従います.
  • 整数をnビットの2進数
  • に変える
  • 整数が正数またはゼロの場合、そのままで格納される.負数の場合、コンピュータはその補コードを取って格納します.

  • 整数をバイナリ符号化形式で復元するには、コンピュータは次の手順に従います.
  • 最左ビットが1である場合、コンピュータはその補符号をとる.左端が0の場合、コンピュータは動作しません.
  • コンピュータは、この整数を10進数
  • に変換する.
    バイナリ符号表現は0が1つしかありません.
       
    ビット論理演算子
    1°バイナリ逆符号またはビット別逆符号:~
    2°ビットと(AND):&
    各ビットについては、2つのオペランドの対応するビットが1の場合のみ結果が1になります(真/偽で記述され、2つのビットのオペランドが真の結果である場合のみ).Cには、ビット対-付与演算子の組合せもあります:&=、例:
    val &= 0377;
    val = val & 0377;

    3°ビットまたは(OR):|
    各ビットについて、任意のオペランドの中で対応するビットが1である場合、結果ビットは1である(真/偽で記述され、いずれかのビットオペランドが真である場合、または両方が真である場合、結果は真である).Cには、組み合わせられたビットまたは-割付演算子もある:|=
    4°ビット異OR:ˆ
    各ビットについて、オペランドの対応するビットが1である(ただし、すべてが1ではない)場合、結果は1である(真/偽で記述され、2つのビットオペランドのうち1つが真であるが、いずれも真ではない場合、結果は真である).Cには、複数のビット異または-付与演算子がある:ˆ=
    4つのビット演算子はcharを含む整数データに使用されます.これらの演算子をビット演算子と呼ぶのは、左右のビットに影響を及ぼさずに各ビットを操作するためです.これらの演算子を通常の論理演算子と混同しないでください(&、|、!)、通常の論理演算子は値全体を操作します.
    シフト演算子
    1°左シフト:<<
    左シフト演算子<<左側のオペランドの値の各ビットを左に移動し、移動するビット数は右側のオペランドで指定します.空のビットは0で埋められ、左側のオペランドの末端に移動したビットは破棄されます.左シフト-代入演算子(<<=)を使用して、変数の値を実際に変更できます.この演算子は、変数内のビットを右の値サイズの位置に左に移動します.
    2°右シフト:>>
    右シフト演算子>>左側のオペランドの値の各ビットを右に移動し、移動するビット数は右側のオペランドで指定します.左側のオペランドの右端に移動したビットを破棄します.unsignedタイプの場合、左端が空いているビットに0を入力します.シンボルタイプの場合、結果はマシンに依存します.空のビットは0で埋めたり、記号(左端)のビットのコピーで埋めたりできます.右に移動-代入演算子(>>=)は、左の変数のビットを指定した数の位置に右に移動します.
    シフト演算子は、2のべき乗に対する高速で効率的な乗算と除算を提供します.
    number << n
    numberに2を乗じたn乗
    number >> n
    numberが負でない場合はnumberを2のn乗で割る
       
    ビットフィールド
    ビットフィールドは、signed intまたはunsigned intの隣接するビットのセットです(C 99では_Boolタイプのビットフィールドも許可されます).ビットフィールドは、各フィールドにラベルを提供し、フィールドの幅を決定する構造宣言によって作成されます.たとえば、次の4つの1ビットワードセグメントが作成されます.
    struct {
         unsigned int autfd: 1;
         unsigned int bldfc: 1;
         unsigned int undln: 1;
         unsigned int itals: 1;
    } prnt;

    ↓通常の構造メンバー演算子を使用して、個別のフィールドに値を割り当てます.
    prnt.itals = 0;
    prnt.undln = 1;

    prnt変数はintサイズのメモリセルに格納されるが、そのうちの4ビットのみが呼び出される.
    フィールドは1ビットサイズに限定されません.
    struct {
         unsigned int code1: 2;
         unsigned int code2: 2;
         unsigned int code3: 8;
    } prcode;

    ↓このコードは、2つの2ビットフィールドと1つの8ビットフィールドを作成します(フィールドの容量を超えていないことを確認する必要があります).
    prcode.code1 = 0;
    prcode.code1 = 3;
    prcode.code1 = 102;

    質問:宣言した総桁数がunsigned intサイズを超えた場合、何が起こりますか?
    答え:次のunsigned intストレージロケーションが使用されます.1つのフィールドが2つのunsigned int間の境界を越えることは許されません.コンパイラは、unsigned int境界でフィールドが整列するように、このようなフィールド定義を自動的にシフトします.
    この場合、最初のunsigned intに名前のない穴が残ります.
    名前の付いていないフィールドの幅を使用して、名前の付いていない穴を塗りつぶすことができます.0の幅を持つ名前のないフィールドを使用して、次のフィールドを次の整数に整列させます.
    struct {
          unsigned int field1: 1;
          unsigned int       : 2;
          unsigned int field2: 1;
          unsigned int       : 0;
          unsigned int field3: 1;
    } stuff;    // stuff.field1 stuff.field2     2    ,stuff.field3      int 

    ビットフィールドは、移植が困難であることが多いため、特定のハードウェアデバイスで使用される正確なフォーマットに従ってデータを格納するなど、移植不可能な用途に使用される.