C言語のきほん|プログラミングで使う進数の基本

数字の見え方が変わると、C言語の理解がぐっと深まる。2進数・8進数・16進数をやさしく整理しよう。

C言語を学んでいると、普段の生活ではあまり見かけない数字の表記が出てきます。
それが 2進数、8進数、16進数 です。

最初は少し難しく感じやすいところですが、ここはとても大切な基礎です。
なぜなら、コンピュータの内部ではデータを 0 と 1 で扱っていて、その世界とC言語のコードがつながっているからです。

特にC言語では、メモリの内容、ビット演算、文字コード、機器制御などで 16進数 がよく登場します。
8進数は昔ほど登場頻度は高くありませんが、C言語の整数定数のルールとして知っておくと安心です。

ここでは、10進数との違い、変換の考え方、C言語での書き方まで、順番にやさしく解説していきます。
数字が苦手でも大丈夫です。表や図のイメージで理解できるように、丁寧に整理していきましょう。

進数とは何か

私たちが普段使っている数は 10進数 です。
0 から 9 までの10種類の数字を使って数を表します。

一方、プログラミングでは用途に応じて別の進数も使います。

進数使う数字特徴
10進数0~9普段の生活で使う表記
2進数0, 1コンピュータ内部の基本表現
8進数0~72進数を3桁ごとにまとめやすい
16進数0~9, A~F2進数を4桁ごとにまとめやすい

ここで大事なのは、同じ値でも表記のしかたが違うだけ ということです。
たとえば 10進数の 123 は、2進数や16進数で表すこともできます。

2進数の基本

2進数は、0 と 1 の2つだけで数を表す方法です。
コンピュータは電気的な ON / OFF の2状態をもとに動いているため、内部ではこの 2進数 が基本になります。

たとえば、10進数の 5 は 2進数では 101 です。

101(2) = 1×2² + 0×2¹ + 1×2⁰ = 4 + 0 + 1 = 5

2進数は右端から順に、1、2、4、8、16… という重みを持っています。
この考え方がわかると、変換の仕組みも理解しやすくなります。

C言語でのポイント

学習の基本としては、C言語では 2進数をそのまま整数定数として書く場面はあまり使わない、と覚えておくとよいです。
その代わり、2進数の考え方を理解して、実際のコードでは10進数や16進数で書く という場面が多いです。

8進数の基本

8進数は、0 から 7 までを使って数を表す方法です。
2進数を 3桁ずつ まとめると対応しやすいため、昔から使われてきました。

C言語では、整数定数の先頭に 0 を付けると 8進数として扱われます。

例:

  • 0173

この書き方は便利ですが、初心者のうちは見落としやすいポイントでもあります。
たとえば 0123 は、見た目は 123 に似ていますが、C言語では 8進数です。

16進数の基本

16進数は、0~9 に加えて A~F を使って数を表します。

  • A は 10
  • B は 11
  • C は 12
  • D は 13
  • E は 14
  • F は 15

C言語では、先頭に 0x または 0X を付けると 16進数になります。

例:

  • 0x7B
  • 0X7B

16進数がよく使われる理由は、2進数との相性がとてもよいからです。
2進数4桁が16進数1桁に対応するので、長い2進数を短く読みやすく書けます。

1111 0110(2) → F6(16)

この対応は、メモリの値やビット列を扱うときにとても便利です。

10進数 123 を変換してみよう

ここからは、10進数の 123 を例にして、2進数、8進数、16進数のつながりを見ていきます。
1つの数字を別の表し方で見られるようになると、進数の理解が一気に進みます。

10進数から2進数への変換

10進数を2進数に変換するときは、2で割って余りを逆順に並べる方法を使います。

手順

  1. 数を 2 で割る
  2. 商と余りを記録する
  3. 商が 0 になるまで繰り返す
  4. 余りを下から上に読む

123 の変換例

2進数から8進数への変換

2進数から8進数に変換するときは、右から3桁ずつ区切る のがコツです。
8 は 2³ なので、2進数3桁と8進数1桁が対応します。

123 の2進数 1111011 を使ってみます。

3桁ずつ区切る

1 111 011

先頭をそろえるために 0 を補うと、さらに見やすくなります。

001 111 011

各ブロックを数値に直す

  • 001 は 1
  • 111 は 7
  • 011 は 3

つまり、

123(10) = 173(8)

このように、8進数は 2進数を3桁ごとにまとめて読める表記だと考えると理解しやすいです。

2進数から16進数への変換

2進数から16進数に変換するときは、右から4桁ずつ区切る のがポイントです。
16 は 2⁴ なので、2進数4桁と16進数1桁が対応します。

同じく 1111011 を使います。

4桁ずつ区切る

111 1011

先頭を 0 で補うと、

0111 1011

各ブロックを16進数に直す

  • 0111 は 7
  • 1011 は 11 なので B

したがって、

123(10) = 7B(16)

16進数の便利さはここにあります。
2進数だと長く見える数でも、16進数にするとかなり読みやすくなります。

変換の流れを図で整理する

ここまでの流れを、1つの図としてまとめるとこんなイメージです。

4ビットと16進数の対応表

4桁の2進数と1桁の16進数の対応は、C言語ではとても重要です。
ここは毎回計算するより、表を見ながら少しずつ覚えていくのがおすすめです。

10進数2進数16進数
000000
100011
200102
300113
401004
501015
601106
701117
810008
910019
101010A
111011B
121100C
131101D
141110E
151111F

特によく出てくるのはこのあたりです。

  • 1010 = A
  • 1011 = B
  • 1111 = F
  • 11111111 = FF

この対応がスムーズに読めるようになると、ビット操作やメモリの値を扱う場面でかなり強くなれます。

C言語での書き方

C言語では、同じ値でも表記のしかたを変えられます。
整数定数として書くときのルールを整理しておきましょう。

表記意味
12310進数123
01738進数10進数で123
0x7B16進数10進数で123

ここで特に注意したいのが、先頭の 0 です。

  • 123 は10進数
  • 0123 は8進数

この違いは、C言語ではとても大事です。
コードを書くときも読むときも、先頭の 0 の有無は必ず意識しておきましょう。

サンプルプログラムで確認する

ファイル名:4_6_1.c

#include <stdio.h>

int main(void)
{
    /* 同じ値を10進数、8進数、16進数で表す */
    int decimal_value = 123;
    int octal_value = 0173;
    int hex_value = 0x7B;

    /* 10進数として表示して比較する */
    printf("10進数の 123 は: %d\n", decimal_value);
    printf("8進数の 0173 を10進数で表示すると: %d\n", octal_value);
    printf("16進数の 0x7B を10進数で表示すると: %d\n", hex_value);

    /* 16進数として表示する例 */
    printf("10進数の 123 を16進数で表示すると: %X\n", decimal_value);

    return 0;
}

実行結果例

10進数の 123 は: 123
8進数の 0173 を10進数で表示すると: 123
16進数の 0x7B を10進数で表示すると: 123
10進数の 123 を16進数で表示すると: 7B

サンプルプログラムの解説

同じ値を別の表記で書いている

int decimal_value = 123;
int octal_value = 0173;
int hex_value = 0x7B;

この3つは、見た目は違ってもすべて同じ値を表しています。
C言語では、値そのものと、どう表記するかは分けて考えることが大切です。

ここが理解できると、コード中に 0xFF や 077 のような数字が出てきても、落ち着いて読めるようになります。

printf で表示形式を変える

printf("10進数の 123 を16進数で表示すると: %X\n", decimal_value);

ここでは %X を使って、10進数の値を16進数として表示しています。
表示形式を変えるだけで、同じ値を別の見え方で確認できるのがポイントです。

よく使う表示指定子は次のようなイメージです。

書式指定子表示のしかた
%d10進数で表示
%o8進数で表示
%x16進数で表示(a~fは小文字)
%X16進数で表示(A~Fは大文字)

たとえば、decimal_value を %o で表示すれば 173、%X で表示すれば 7B と表示されます。

よくつまずきやすいポイント

先頭の 0 を見落とす

これはC言語で本当によくあるポイントです。

int n = 0123;

この値は 10進数の123ではありません。
先頭に 0 があるので 8進数として解釈され、10進数では 83 になります。

逆に、10進数の123を入れたいならこう書きます。

int n = 123;

見た目が似ているので、慣れるまでは注意して見ていくのがおすすめです。

16進数の A~F は数字の続き

16進数の A~F は、文字に見えますが意味としては数字です。

  • A = 10
  • B = 11
  • C = 12
  • D = 13
  • E = 14
  • F = 15

最初は少し違和感がありますが、16進数では自然なルールです。
英字が出てきても、数字の一部として読むクセをつけるとスムーズになります。

2進数は書かなくても理解は必要

C言語のコードで2進数を直接書かない場面でも、2進数の考え方はとても大事です。
特にビット演算やフラグ処理では、16進数を見て2進数の形をイメージできると理解が一気に進みます。

たとえば、

0x0F = 00001111
0xF0 = 11110000

このような対応が頭に浮かぶと、AND や OR の動きも読みやすくなります。

覚え方のコツ

進数の学習は、最初から全部を完璧に計算しようとしなくて大丈夫です。
むしろ、対応関係を見ながら慣れていくほうがスムーズです。

2進数と16進数の対応を優先する

まずは 4ビット と 16進数1桁 の対応を少しずつ覚えるのがおすすめです。
ここがわかると、実際のコードを読む力がぐっと上がります。

8進数は 3桁区切り のルールだけでも十分役立つ

8進数は使用頻度はやや低めですが、C言語の整数定数として出てくることがあります。
2進数を3桁ずつ区切る、というルールを覚えておくだけでもかなり助かります。

同じ値をいろいろな表記で見てみる

たとえば 15 や 31 のような小さめの数字で、10進数、2進数、8進数、16進数を対応させてみると感覚がつかみやすいです。

15(10) =  1111(2) = 17(8) =  F(16)
31(10) = 11111(2) = 37(8) = 1F(16)

こうした小さな練習を積み重ねると、数字の見え方が自然につながっていきます。