C言語基礎|進数と基数

型の学習に入る前に、まずは「整数の表し方」の基本を押さえましょう。
C言語で整数を扱うとき、私たちが普段使っている 10進数 だけでなく、8進数16進数の表記もよく出てきます。さらに、コンピュータの世界の根っこには 2進数 があります。

ここを理解しておくと、ビット演算、フラグ管理、メモリアドレス表示などがグッと読みやすくなりますよ。

基数とは何か

基数は「その数の世界で使える数字の種類の数」です。

  • 10進数の基数は 10(0〜9 の10種類)
  • 2進数の基数は 2(0 と 1 の2種類)
  • 8進数の基数は 8(0〜7 の8種類)
  • 16進数の基数は 16(0〜9 と A〜F の16種類)

表:基数と使える数字

進数基数使える数字
2進数20, 1
8進数80〜7
10進数100〜9
16進数160〜9, A〜F

この表のポイントは、「基数ぶんの数字を使い切ると繰り上がる」というルールが全進数共通だということです。

なぜコンピュータは2進数が得意なの?

コンピュータの回路は「電気が流れている/流れていない」を扱うのが基本です。
つまり ON / OFF、1 / 0 の2種類が自然で、2進数はハードウェアと相性が最高なんですね。

ただし 2進数は桁数が長くなりがちで、人間には読みにくい…。
そこで 8進数や16進数が便利に使われます。

2進数を人間が読みやすくする工夫

表記まとまり便利な理由
8進数2進数3桁ごと3ビットを1桁で表せる。
16進数2進数4桁ごと4ビット(1ニブル)を1桁で表せる。

0〜20の対応表(10進・8進・16進・2進)

提示された Table 7-1 は「同じ値を進数ごとにどう書くか」を並べた表です。
この表を眺めると、繰り上がりのタイミングがよく分かります。

数と基数(0〜20)

10進数8進数16進数2進数
0000
1111
22210
33311
444100
555101
666110
777111
81081000
91191001
1012A1010
1113B1011
1214C1100
1315D1101
1416E1110
1517F1111
16201010000
17211110001
18221210010
19231310011
20241410100

この表の読み方(見どころ)

  • 10進数で 8 になった瞬間、8進数は 10 に繰り上がる。
  • 10進数で 16 になった瞬間、16進数は 10 に繰り上がる。
  • 2進数は繰り上がりが頻繁で、どんどん桁が増える。

10進数・8進数・16進数の繰り上がりをイメージする

10進数の繰り上がり

10種類(0〜9)を使い切ると次の桁へ。

  • 9 の次は 10
  • 99 の次は 100

8進数の繰り上がり

8種類(0〜7)を使い切ると次の桁へ。

  • 7 の次は 10
  • 77 の次は 100

16進数の繰り上がり

16種類(0〜9, A〜F)を使い切ると次の桁へ。

  • F の次は 10
  • FF の次は 100

C言語での進数表記(ここが実用ポイント)

C言語のソースコード上で直接書ける整数リテラル(整数の書き方)は主にこの3つです。

C言語の整数リテラルの書式

進数書式(例)意味
10進数175310進の 1753
8進数0758進の 75(10進だと 61)
16進数0x1A16進の 1A(10進だと 26)

※ C言語では、先頭が 0 の整数は 8進数扱いになります。慣れないうちはここで事故りやすいです。

サンプルプログラム

ここでは LEDの点灯パターン(8個)を例にして、10進・16進・2進の見え方を体感できるプログラムを例に解説をします。

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

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

// 進数の表記を確認する(10進・16進・2進っぽい表示)

#include <stdio.h>

void print_bits8(unsigned int x)
{
    for (int i = 7; i >= 0; i--) {
        putchar((x & (1u << i)) ? '1' : '0');
    }
}

int main(void)
{
    unsigned int pattern;

    puts("8ビットの点灯パターンを10進数で入力してください。");
    printf("入力(0〜255): ");
    scanf("%u", &pattern);

    printf("10進数: %u\n", pattern);
    printf("16進数: 0x%02X\n", pattern);
    printf("2進数: ");
    print_bits8(pattern);
    putchar('\n');

    puts("16進数は2進数4桁ごとの省略表記だと思うとラクです。");

    return 0;
}

このプログラムで登場する命令の書式と役割

puts

  • 書式:puts(文字列);
  • 役割:文字列を表示して改行する(簡単な案内表示に便利)

printf

  • 書式:printf(書式文字列, 値, ...);
  • 役割:書式を指定して表示する。
    ・%u は unsigned int を10進で表示
    ・%02X は16進(大文字)で2桁表示、足りない分は0で埋める。

scanf

  • 書式:scanf(書式文字列, 変数のアドレス);
  • 役割:キーボード入力を変数に読み込む。
    ・%u は unsigned int の入力
    ・&pattern のようにアドレスが必要

putchar

  • 書式:putchar(文字);
  • 役割:文字を1文字だけ出力する(ビットを1個ずつ出すのに便利)

ビット演算(おまけで出てくるけど重要)

  • x & (1u << i)
    ・役割:x の i ビット目が 1 かどうかを調べる

表や図を使った説明(print_bits8 の動き)

図:8ビットの各桁(左が上位ビット)

この図の意味は、「2進数の各桁は 2の累乗の重みを持つ」ということです。
たとえば 10100010 なら、

  • 128 + 32 + 2 = 162

みたいに足し算で10進値に戻せます。