第14章の1 ビット単位の演算子

基本的な演算子については第4章で説明しましたが、この章では、もう少し複雑な演算子について説明します。まず、ビット単位の論理演算子とシフト演算子を取り上げます。

1. ビット単位の論理演算子

ビット単位でデータ操作をするものです。対象は整数に限られます

【ビット演算子】
演算子説明
 &ビットごとの AND
 |ビットごとの OR
 ^ビットごとの XOR
 ~ビットごとの反転(1 の補数)
 <<左シフト
 >>右シフト

(1) &(AND)

0 & 0 → 0
0 & 1 → 0
1 & 0 → 0
1 & 1 → 1

両方のビットが 1 のときのみ結果が 1 になるビット演算です。

10101010 and 11110000 ーーーーーーー 10100000

必要なビット以外をOFF(0)にする処理(マスクといいます)に使用されます。

例えば、10101010 という1バイトのビット列の下位4ビットを OFF する場合、そのままにしたいビットを 1 、OFFしたいビットを 0 にした、11110000 で and することにより実現できます。

unsigned char a = 0xaa;      //     10101010
printf("%#x\n",a & 0xf0);    // and 11110000

【実行結果例】
0xa0

(2) |(OR)

0 | 0 → 0
0 | 1 → 1
1 | 0 → 1
1 | 1 → 1

いずれかのビットが 1 なら結果が 1 になるビット演算です。

10101010 or 11110000 ーーーーーーー 11111010

必要なビットをON(1)にする場合に or は使われます。

例えば、10101010 という1バイトのビット列の上位4ビットを ON する場合、ONにしたいビットを 1 、そのままにしたいビットを 0 にした、11110000 で or することにより実現できます。

unsigned char a = 0xaa;      //    10101010
printf("%#x\n",a | 0xf0);    // or 11110000

【実行結果例】
0xfa

(3) ^(XOR)

0 ^ 0 → 0
0 ^ 1 → 1
1 ^ 0 → 1
1 ^ 1 → 0

両方のビットが異なるときに結果を 1 にするビット演算です。

10101010 xor 00001111 ーーーーーーー 10100101

特定なビットを反転する場合に xor は使われます。

例えば、10101010 という1バイトのビット列の下位4ビットを反転する場合、反転したいビットを 1 、そのままにしたいビットを 0 にした、00001111 で xor することにより実現できます。

unsigned char a = 0xaa;      //     10101010
printf("%#x\n",a ^ 0x0f);    // xor 00001111

【実行結果例】
0xa5

(4) ~(補数)

0 → 1
1 → 0

両方のビットが異なるときに結果を 1 にするビット演算です。

全ビットの無条件反転を行います。

unsigned char a = 0xaa;      // 10101010
printf("%#x\n",~a);

【実行結果例】
0xffffff55

printf関数が unsigned char型を int型に符号拡張するために、0xffffff55 と表示されます。

〇演習問題

任意の 2つの unsigned int型で表すことのできる整数値を入力し、 論理積(AND)、論理和(OR)、排他的論理和(XOR)を求めて表示せよ。
ただし、表示はわかりやすいように16進数で行うこと。

【実行結果例】
0~4294967295の整数を2つ入力
65530
65535
65530(0xfffa) AND 65535(0xffff) = 0xfffa
65530(0xfffa) OR 65535(0xffff) = 0xffff
65530(0xfffa) XOR 65535(0xffff) = 0x5

※ 水色字はキーボードからの入力

解答例

#include <stdio.h>
#include <limits.h>

int main(void)
{
    unsigned int x, y, z;

    printf("0~%uの整数を2つ入力\n", UINT_MAX);
    scanf("%u%u", &x, &y );

    z = x & y;
    printf("%u(%#x) AND %u(%#x) = %#x\n", x, x, y, y, z);

    z = x | y;
    printf("%u(%#x) OR  %u(%#x) = %#x\n", x, x, y, y, z);

    z = x ^ y;
    printf("%u(%#x) XOR %u(%#x) = %#x\n", x, x, y, y, z);

    return 0;
}
  • %u は、unsigned型に対する書式指定文字列です。
  • UINT_MAX は limits.h の中でマクロ定義され、unsigned int型の最大値を表します。

2. シフト演算子

(1) <<(左シフト)

  • x << n と書き、x を n ビット左へシフトします。
  • 右側の空いたビットには 0 が入り、左側のビットは捨てられます。
  • 左シフトは x が正の場合あふれがなければ、x << 1 で「x * 2」を計算することと同じになります。
// 正の整数のとき
int x = 100;
x = x << 2;
printf("%#x\n", x);

【実行結果例】
0x190

(2) >>(右シフト)

  • x >> n と書き、x を n ビット右へシフトします。
  • 左側の空いたビットには、x が符号無しなら 0 が入ります。x が符号付きなら、算術シフトを行う処理系では符号桁が入り、論理シフトを行う処理系では 0 が埋められます。 右側のビットは算術シフト、論理シフトにかかわらず捨てられます。
  • 負の数のシフトは処理系依存です。扱いには注意が必要です。
  • 右シフトは x が正の場合あふれがなければ 、x >> 1 で「x / 2」を計算することと同じになります。
// 正の整数のとき
int x = 100;
x = x >> 2;
printf("%#x\n", x);

【実行結果例】
0x19


// 負の整数のとき
int x = -100;
x = x >> 2;
printf("%#x\n", x);

【実行結果例】
0xffffffe7

  • 算術シフト:数値の演算を行うときに使用するシフト演算で、シフトの際に最上位の符号ビットを保存するシフトです。
  • 論理シフト:ビットの位置を変えるときに使用するシフト演算で、シフトの際に最上位の符号ビットを考慮することなくシフトを行います。

〇 演習問題

次の手順で、整数値以外の値が入力されるまで、任意の unsigned int型で表すことのできる整数値を入力し、 そのビットパターンを表示するプログラムを作成しなさい。

  1. main関数で次の処理を行う。
    • 任意のunsigned int型で表すことのできる10進整数を入力する。入力した整数を引数にして、ビットパターン表示関数 bit_disp( ) を呼ぶ。
    • 整数値以外の値が入力されたら、プログラムを終了する。
  2. bit_disp関数の仕様は以下である。
    • 関数宣言:void bit_disp( unsigned int dt );
    • 引数: dt … 任意の 符号無し10進整数
    • 返却値:なし
    • 処理:unsigned int ビットサイズ分ループする。最上位ビットから順にビットパターン(0 or 1)を表示する。

【実行結果例】
0~4294967295の整数を入力(終了条件:整数値以外)
0
0 —> 00000000000000000000000000000000
32767
0x7fff —> 00000000000000000111111111111111
4294967295
0xffffffff —> 11111111111111111111111111111111
a

※ 水色字はキーボードからの入力

解答例

#include <stdio.h>
#include <limits.h>

void bit_disp(unsigned int dt);

int main(void)
{
    unsigned int a;

    printf("0~%uの整数を入力(終了条件:整数値以外)\n", UINT_MAX);
    while(scanf("%u", &a ) == 1){
        bit_disp(a);
    }
    
    return 0;
}

/*** ビット表示関数 ***/
void bit_disp(unsigned int dt)
{
    int i, len;

    // unsigned int のビット数をlenにセット
    len = sizeof(dt) * CHAR_BIT;
    
    printf("%#x ---> ", dt);
    for (i = len - 1; i >= 0; i--){
        // ビットの表示
        printf("%u", (dt >> i) & 0x0001);
    }
    printf("\n");
}

CHAR_BIT は limits.h の中でマクロ定義され、1バイトのビット数を表します。

コメント