C言語基礎|ビットとCHAR_BIT

C言語で変数を「箱」だとすると、その箱の中身は 0 と 1 の並び(ビット)です。
つまり、数値も文字も、最終的にはビット列として保存されています。

ここで最初に押さえたいのはこの2つ。

  • ビット(bit):0 か 1 のどちらかを表す最小単位
  • char は “最低でも” 8ビット(でも 何ビットかは処理系依存

そこで登場するのが CHAR_BIT です。
これは「この環境の char は何ビットでできてる?」を教えてくれる定数(マクロ)になります。

ビットって何?(C的なイメージ)

Cの世界では、ビットはざっくりこう捉えると分かりやすいです。

  • 2種類の状態(0/1)を区別できる最小単位
  • ビット1個ずつにアドレスが付いていなくてもOK(ビット単位で直接参照できなくてもOK)
  • 0 以外にすることを **セット(set)**と呼ぶ

図:箱の中はビット列

この図は「中身は0/1の並び」というイメージを作るためのものです。
次に、この“並びの本数”が重要になります。

CHAR_BIT:char の箱は何ビット?

CHAR_BIT は <limits.h> に定義されていて、意味はこれです。

1バイト(= char 1個)が何ビットか

CHAR_BIT の意味

名前意味どこで定義される
CHAR_BIT1バイトに含まれるビット数<limits.h>

例として、もし CHAR_BIT が 8 なら、

  • char の箱は 8ビット
  • 0/1 の組み合わせは 2^8 = 256通り

となり、unsigned char なら 0~255 が表せる、という話につながります。

図:CHAR_BIT = 8 のときの char

この図は「CHAR_BIT 個のビットの束が char 1個」という関係を示しています。

値の範囲と CHAR_BIT の関係(なぜ重要?)

char のビット数が増えれば、表現できる値の種類も増えます。

ビット数と表現できる値の数

ビット数表現できるパターン数unsigned の範囲(例)
82^8 = 2560 ~ 255
92^9 = 5120 ~ 511
162^16 = 655360 ~ 65535

この表が言いたいのは、char の範囲が処理系依存なのは、そもそも char のビット数が処理系依存だからってことです。

sizeof 演算子:箱の大きさを調べる

Cでは sizeof(char) は必ず 1 と決め打ちされています。
ただしこの 1 は「1バイト」であって、「8ビット」とは限りません。
(8ビットが多いけど、規格上は CHAR_BIT で決まる)

sizeof の役割

書式意味
sizeof(型名)その型が何バイトか
sizeof 変数名その変数が何バイトか

そして int 系のサイズは環境依存です。ただし順序関係は決まっています。

int系のサイズ関係

関係意味
sizeof(short) ≤ sizeof(int) ≤ sizeof(long) ≤ sizeof(long long)右側ほど同じか大きい

さらに、符号付きと符号無しでサイズは同じです。

signed/unsigned でサイズは同じ

成立する関係
short と unsigned shortsizeof(short) = sizeof(unsigned short)
int と unsignedsizeof(int) = sizeof(unsigned)

サンプルプログラム

CHAR_BIT と “ビット換算したサイズ”まで一気に見えるプログラムです。

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

// この環境の 1バイトが何ビットか、各整数型が何ビットかを表示する

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

int main(void)
{
    puts("=== ビット数チェック ===");

    printf("1バイトのビット数 (CHAR_BIT) = %d\n", CHAR_BIT);

    printf("char      : %zu バイト = %zu ビット\n", sizeof(char),      sizeof(char)      * CHAR_BIT);
    printf("short     : %zu バイト = %zu ビット\n", sizeof(short),     sizeof(short)     * CHAR_BIT);
    printf("int       : %zu バイト = %zu ビット\n", sizeof(int),       sizeof(int)       * CHAR_BIT);
    printf("long      : %zu バイト = %zu ビット\n", sizeof(long),      sizeof(long)      * CHAR_BIT);
    printf("long long : %zu バイト = %zu ビット\n", sizeof(long long), sizeof(long long) * CHAR_BIT);

    return 0;
}

このプログラムは、

  • CHAR_BIT で「1バイト=何ビット?」を確認し
  • sizeof で「何バイト?」を確認し
  • 掛け算して「何ビット?」まで見える
    という流れをまとめて確認できます。

登場する命令の書式と「何をするか」

#include

  • 書式:#include <ヘッダ名>
  • 何をする:ライブラリ関数やマクロ定義を取り込む
    <stdio.h>:puts / printf を使う
    <limits.h>:CHAR_BIT を使う

puts

  • 書式:puts(文字列);
  • 何をする:文字列を表示して改行する(見出し向き)

printf

  • 書式:printf(書式文字列, 値, ...);
  • 何をする:値を指定した形式で表示する

sizeof(演算子)

  • 書式:sizeof(型名) / sizeof 変数名
  • 何をする:型や変数の大きさ(バイト数)を得る
  • ポイント:sizeof(char) は必ず 1

%zu(printf の書式指定)

  • 何をする:sizeof の結果(size_t 型)を表示するための指定
  • つまり %d ではなく %zu を使うのが安全、という話につながります

図でまとめ:バイトとビットの関係

図:1バイトは CHAR_BIT 個のビット

この図は「バイトはビットの束」であることを示しています。
だから、型のビット数は「sizeof(型) * CHAR_BIT」で求められます。