C言語基礎|整数型の内部表現

整数の中身は 0 と 1 の並びだった!ビットを読めると、バグも計算も一段ラクになる。

整数型の内部表現って何を学ぶの?

整数型の変数って、見た目は 25 とか 171 とか「普通の数」ですが、メモリの中では 0 と 1(ビット)の並びで保存されています。
そしてこの「並び方のルール」は、型(unsigned か signed か、何ビットか)によって変わります。

この記事ではまず、符号無し整数型(unsigned 系)を主役にして、
「10進数の値が、どうやってビット列に変換されて保存されるのか」を丁寧に追いかけます。

ポイントはここです。

  • 整数型の内部は 純2進記数法(そのまま2進数)で表せる。
  • ビットには 重み(2のべき乗)がある。
  • n ビットなら表せる範囲は 0 〜 2n − 1
  • ビット数は処理系依存なので、ヘッダやマクロで確認する流れが大事

まず用語を整理:ビット、上位ビット、下位ビット

用語ざっくり説明例(16ビット)
ビット(bit)0 か 1 のどちらかを持つ最小単位0 / 1
下位ビット右側(小さい重み)B0, B1, B2…
上位ビット左側(大きい重み)…B13, B14, B15
第posビット下位から pos 個目のビット第0ビット = B0

図:16ビットの並び方(位置の呼び方)

この図の意味:右端の B0 がいちばん軽い(20)、左へ行くほど重くなる(21, 22…)という並びです。

符号無し整数の内部表現:そのまま2進数を詰める

符号無し整数(unsigned 系)はシンプルで、値を2進数で表して、そのままビットに対応させます。

例:unsigned の 25 を 16ビットで表す

10進数 25 は 2進数で 11001 です。
16ビットにするため、左を 0 で埋めます。

この図の説明

  • 11001 の部分が「値そのもの」
  • 足りない上位側は 0 で埋めて、ビット数を揃えています(ゼロ拡張)

ビット列が表す値は「重み」の合計で決まる

n ビットの符号無し整数の値は、各ビットの重みを足したものです。

表:ビットの重み(下位側の例)

ビット重みビットが1なら足す値
B0201
B1212
B2224
B3238
B42416

25 の計算を“重み”で見る

25 の 16ビット表現は 0000000000011001 なので、1 になっているのは B4, B3, B0 です。

  • B4 = 1 → 16
  • B3 = 1 → 8
  • B0 = 1 → 1

合計:16 + 8 + 1 = 25

ビット列 → 10進数(171の例)

例:8ビットの 10101101 は何?

重みを足します。

  • B7(128) = 1 → 128
  • B5(32) = 1 → 32
  • B3(8) = 1 → 8
  • B2(4) = 1 → 4
  • B0(1) = 1 → 1

合計:128 + 32 + 8 + 4 + 1 = 173

この図の説明
ビット列は「どの重みを足すか」のスイッチ一覧です。1 になっているところだけ足せばOKです。

表現範囲:nビットなら 0 〜 2n − 1

符号無し整数は、nビット全部が 0 のとき最小、全部が 1 のとき最大です。

表:符号無し整数の範囲(代表例)

ビット数 n最小値最大値種類数
8028 − 1 = 25528 = 256
160216 − 1 = 65535216 = 65536
320232 − 1 = 4294967295232
640264 − 1264

この表の説明

  • ビットが増えるほど最大値が爆発的に増えます。
  • 符号無しは「全部 0 が最小」「全部 1 が最大」なので直感的です。

サンプルプログラム

ここでは unsigned char(8ビット相当が多い)を例に、値を 10進・16進・2進風(自作表示)で確認します。

プロジェクト名:chap7-12-1 ソースファイル名:chap7-12-1.c

// 符号無し整数のビット列を確認する(8ビット表示)
#include <stdio.h>

// 8ビットを 0/1 で表示する
void print_bits_u8(unsigned char v)
{
    for (int i = 7; i >= 0; i--) {
        putchar((v & (1u << i)) ? '1' : '0');
    }
}

int main(void)
{
    unsigned char v = 173;   // 例:173

    puts("符号無し整数の中身をのぞいてみよう");

    printf("10進数 : %u\n", (unsigned)v);
    printf("16進数 : 0x%02X\n", (unsigned)v);

    printf("2進数 : ");
    print_bits_u8(v);
    putchar('\n');

    return 0;
}

実行結果例

符号無し整数の中身をのぞいてみよう
10進数 : 173
16進数 : 0xAD
2進数 : 10101101

登場する命令(関数・演算子)の書式と役割

puts(関数)

項目内容
書式puts(文字列);
何をする?文字列を表示して、最後に改行も出す。

printf(関数)

項目内容
書式printf(書式文字列, 値, ...);
何をする?書式に合わせて値を表示する
%u(符号無し10進), %02X(16進2桁大文字)

putchar(関数)

項目内容
書式putchar(文字);
何をする?文字を1文字表示する。

&(ビットAND演算子)

項目内容
書式a & b
何をする?同じ桁で 1 と 1 のときだけ 1 にする(ビット単位)
この記事での役割特定のビットが 1 かどうかを判定する。

<<(左シフト演算子)

項目内容
書式1u << i
何をする?1 のビットを左へ i 個ずらす(マスク作りに便利)
この記事での役割第 i ビットだけが 1 の“チェック用ビット”を作る。

整数型に関する補足(さらっと全体像)

_Bool 型

項目内容
_Bool
0 または 1
便利な別名stdbool.h をインクルードすると bool / true / false が使える

拡張整数型

項目内容
標準整数型char 〜 long long など
拡張整数型処理系が独自に追加できる整数型

stdint.h の役割

目的
ビット幅が明確な型uint8_t, int32_t など
範囲マクロUINT32_MAX など(処理系依存部分を吸収)

まとめ

  • 符号無し整数は 2進数をそのままビットに割り当てる
  • ビットの 1 は「その重みを足すスイッチ」
  • n ビットなら範囲は 0 〜 2n − 1
  • 実機で確認したいときは、2進表示関数を自作すると理解が一気に進みます。