
C言語の基本|2進数・8進数・16進数の基本
同じ値でも書き方はひとつではない。進数のつながりを知って、コンピューターの数字を読み解こう。
私たちが普段の生活で使用している数は、0から9までの数字を使う10進数です。
一方、C言語の学習を進めると、0と1だけで表す2進数、0から7までを使う8進数、0から9とAからFまでを使う16進数が登場します。
最初は、同じ数字なのに表し方がいくつもあるため、少し難しく感じるかもしれません。
しかし、進数が変わっても、値そのものが別のものになるわけではありません。同じ値を、異なる数字と桁の仕組みで表現しているだけです。
たとえば、10進数の123は、次のように表せます。
10進数:123
2進数:1111011
8進数:173
16進数:7B
どれも表し方が違うだけで、値は同じ123です。
C言語では、特に次のような場面で進数の知識が役立ちます。
- コンピューター内部の0と1を理解する
- メモリに保存された値を読む
- ビット演算を理解する
- フラグの組み合わせを確認する
- 文字コードを確認する
- ハードウェアや機器を制御する
- 16進数で表示されたデータを読む
- 8進整数定数の書き方を理解する
このパートでは、進数の基本、桁の重み、10進数からの変換、C言語での書き方、printfによる表示方法を順番に確認します。
数字が苦手でも、桁ごとの仕組みを一つずつ見ていけば大丈夫です。
進数とは数を表すための仕組み
進数とは、何種類の数字を使って値を表すかを決める仕組みです。
10進数では、0から9までの10種類の数字を使います。
0 1 2 3 4 5 6 7 8 9
9の次へ進むと桁が上がり、10になります。
2進数では、0と1の2種類だけを使います。
0 1
1の次へ進むと桁が上がり、10になります。ただし、これは2進数の10なので、10進数では2を表します。
8進数では、0から7までの8種類の数字を使います。
0 1 2 3 4 5 6 7
7の次は10になり、この8進数の10は、10進数では8を表します。
16進数では、0から9に加えてAからFまでを使います。
0 1 2 3 4 5 6 7 8 9 A B C D E F
Fの次は10になり、この16進数の10は、10進数では16を表します。
進数ごとに使用する数字が異なる
| 進数 | 使用する数字 | 桁が上がるタイミング | 主な用途 |
|---|---|---|---|
| 2進数 | 0、1 | 2ごと | コンピューター内部の基本表現 |
| 8進数 | 0から7 | 8ごと | 2進数を3桁ずつまとめる |
| 10進数 | 0から9 | 10ごと | 日常生活で使う一般的な表記 |
| 16進数 | 0から9、AからF | 16ごと | 2進数を4桁ずつまとめる |
進数によって、1桁で表せる範囲が異なります。
| 進数 | 1桁で表せる最小値 | 1桁で表せる最大値 |
|---|---|---|
| 2進数 | 0 | 1 |
| 8進数 | 0 | 7 |
| 10進数 | 0 | 9 |
| 16進数 | 0 | F |
桁にはそれぞれ重みがある
10進数の123は、桁ごとに分けると次のようになります。
1 × 102 + 2 × 101 + 3 × 100
計算すると、次の値になります。
1 × 100 + 2 × 10 + 3 × 1
= 100 + 20 + 3
= 123
右端から順番に、1、10、100という重みを持っています。
2進数では、右端から順番に1、2、4、8、16と重みが増えます。
8進数では、1、8、64、512と増えます。
16進数では、1、16、256、4096と増えます。
| 桁 | 2進数の重み | 8進数の重み | 10進数の重み | 16進数の重み |
|---|---|---|---|---|
| 右から1桁目 | 1 | 1 | 1 | 1 |
| 右から2桁目 | 2 | 8 | 10 | 16 |
| 右から3桁目 | 4 | 64 | 100 | 256 |
| 右から4桁目 | 8 | 512 | 1000 | 4096 |
| 右から5桁目 | 16 | 4096 | 10000 | 65536 |
進数を理解するときは、各桁の数字だけでなく、その桁が持つ重みを考えることが大切です。
図1:進数によって桁の重みが変わる

この図から分かること
同じ桁数でも、進数が違えば、それぞれの桁が持つ重みも変わります。
2進数では右へ1桁移動するごとに重みが2分の1になり、左へ1桁移動するごとに2倍になります。
2進数101は、4の位、2の位、1の位に分けて計算できます。
1 × 4 + 0 × 2 + 1 × 1
= 5
2進数は0と1で数を表す
2進数は、0と1だけを使って値を表す方法です。
コンピューターの電子回路では、電気が流れている状態と流れていない状態など、2つの状態を区別して情報を扱います。
この2つの状態を、数値の1と0に対応させて考えられるため、コンピューター内部では2進数が基本になります。
一般的には、次のようなイメージで説明されます。
| 2進数 | 状態のイメージ |
|---|---|
| 0 | OFF、電気がない、偽 |
| 1 | ON、電気がある、真 |
実際のコンピューター内部はさらに複雑ですが、最初は0と1の2状態でデータを表すと理解しておけば大丈夫です。
2進数の桁の重み
2進数は、右端から順番に次の重みを持ちます。
1、2、4、8、16、32、64、128
これらは2の累乗です。
| 桁 | 2の累乗 | 10進数での重み |
|---|---|---|
| 右から1桁目 | 20 | 1 |
| 右から2桁目 | 21 | 2 |
| 右から3桁目 | 22 | 4 |
| 右から4桁目 | 23 | 8 |
| 右から5桁目 | 24 | 16 |
| 右から6桁目 | 25 | 32 |
| 右から7桁目 | 26 | 64 |
| 右から8桁目 | 27 | 128 |
2進数101を10進数へ変換する
2進数101を、桁ごとの重みに分けてみましょう。
101
右から順番に、1、2、4の重みを割り当てます。
1 × 4
0 × 2
1 × 1
それぞれを足します。
4 + 0 + 1
= 5
したがって、2進数101は10進数の5です。
101(2進数)=5(10進数)
2進数1111011を10進数へ変換する
次に、2進数1111011を確認します。
| 2進数の桁 | 重み | 計算 |
|---|---|---|
| 1 | 64 | 1 × 64 |
| 1 | 32 | 1 × 32 |
| 1 | 16 | 1 × 16 |
| 1 | 8 | 1 × 8 |
| 0 | 4 | 0 × 4 |
| 1 | 2 | 1 × 2 |
| 1 | 1 | 1 × 1 |
1になっている桁の重みを合計します。
64 + 32 + 16 + 8 + 0 + 2 + 1
= 123
したがって、次の対応になります。
1111011(2進数)=123(10進数)
C17では2進整数定数を標準の書き方として使わない
この教材で基準としているC17では、2進整数定数を表す標準の接頭辞はありません。
一部のコンパイラでは、次のような書き方を独自機能として受け付ける場合があります。
0b1111011
しかし、これはC17の標準的な整数定数ではありません。
C17の範囲で移植しやすいコードを書く場合は、同じ値を10進数や16進数で記述します。
int decimal_value = 123;
int hex_value = 0x7B;2進数の考え方は、ビットの並びを理解するために重要です。ソースコードでは16進数を使い、その値を頭の中で2進数に対応させる場面が多くあります。
8進数は0から7までを使う
8進数は、0から7までの8種類の数字を使います。
0 1 2 3 4 5 6 7
8進数では7の次に桁が上がり、10になります。
8進数の10は、10進数では8です。
| 8進数 | 10進数 |
|---|---|
| 0 | 0 |
| 1 | 1 |
| 7 | 7 |
| 10 | 8 |
| 11 | 9 |
| 17 | 15 |
| 20 | 16 |
8進数の桁の重み
8進数の桁は、右から順番に次の重みを持ちます。
1、8、64、512、4096
| 桁 | 8の累乗 | 10進数での重み |
|---|---|---|
| 右から1桁目 | 8⁰ | 1 |
| 右から2桁目 | 8¹ | 8 |
| 右から3桁目 | 8² | 64 |
| 右から4桁目 | 8³ | 512 |
8進数173を10進数へ変換すると、次のようになります。
1 × 82 + 7 × 81 + 3 × 80
計算すると、次の値になります。
64 + 56 + 3
= 123
したがって、8進数173は10進数の123です。
C言語では先頭の0が8進数を表す
C言語で8進整数定数を書くときは、先頭に0を付けます。
0173
C言語のソースコードに書かれた0173は、8進数の173という意味です。
その値は、10進数では123です。
int octal_value = 0173;先頭の0も含めて、8進整数定数を表すための記述です。
8進整数定数に8や9は使えない
8進数で使用できる数字は0から7までです。
そのため、次の表記は正しくありません。
08
09
先頭が0なので8進数として解釈されますが、8進数では8と9を使用できません。コンパイルエラーになります。
10進数の先頭へ不要な0を付けない
次の2つは、見た目が似ていますが値が異なります。
int first_value = 123;
int second_value = 0123;first_valueへ入るのは10進数の123です。
second_valueへ入るのは8進数の123であり、10進数では83です。
| ソースコード上の表記 | 進数 | 10進数での値 |
|---|---|---|
| 123 | 10進数 | 123 |
| 0123 | 8進数 | 83 |
桁をそろえる目的で、10進数の先頭へ0を付けるのは避けましょう。
16進数は0からFまでを使う
16進数では、0から9までの数字に加えて、AからFまでの英字を使います。
| 16進数 | 10進数 |
|---|---|
| 0 | 0 |
| 1 | 1 |
| 9 | 9 |
| A | 10 |
| B | 11 |
| C | 12 |
| D | 13 |
| E | 14 |
| F | 15 |
16進数では、Fの次に桁が上がって10になります。
16進数の10は、10進数では16です。
英字のAからFは、文字としてではなく、10から15までの数字として使われています。
16進数の桁の重み
16進数の桁は、右から順番に次の重みを持ちます。
1、16、256、4096、65536
| 桁 | 16の累乗 | 10進数での重み |
|---|---|---|
| 右から1桁目 | 16⁰ | 1 |
| 右から2桁目 | 16¹ | 16 |
| 右から3桁目 | 16² | 256 |
| 右から4桁目 | 16³ | 4096 |
16進数7Bを10進数へ変換してみましょう。
Bは10進数の11です。
7 × 161 + B × 160
Bを11として計算します。
7 × 16 + 11 × 1
= 112 + 11
= 123
したがって、16進数7Bは10進数の123です。
C言語では0xまたは0Xを付ける
C言語で16進整数定数を書くときは、先頭に0xまたは0Xを付けます。
0x7B
0X7B
どちらも同じ値です。
AからFについても、大文字と小文字のどちらを使っても構いません。
0x7b
0x7B
0X7b
0X7B
これらは、すべて10進数の123を表します。
16進数がよく使われる理由
16進数は、2進数との対応が分かりやすいという特徴があります。
2進数4桁で表せる範囲は、次のとおりです。
0000から1111
10進数では0から15までです。
16進数の1桁も、0からFまでの16種類です。そのため、2進数4桁と16進数1桁を正確に対応させられます。
0000 → 0
0001 → 1
1010 → A
1111 → F
長い2進数を4桁ずつ区切り、16進数へ置き換えると短く読みやすくなります。
図2:10進数123を2進・8進・16進へ変換する

この図から分かること
10進数の123を2進数で表すと1111011です。
この2進数を右から3桁ずつ区切ると、8進数の173へ変換できます。
右から4桁ずつ区切ると、16進数の7Bへ変換できます。
8は23、16は24なので、次の対応が成り立ちます。
- 2進数3桁は8進数1桁に対応する
- 2進数4桁は16進数1桁に対応する
10進数123を2進数へ変換する
10進数を2進数へ変換する代表的な方法は、2で割り続けて余りを記録する方法です。
手順は次のとおりです。
- 10進数を2で割る
- 商と余りを記録する
- 商が0になるまで2で割り続ける
- 余りを最後から逆順に読む
123を2で割っていきます。
| 計算 | 商 | 余り |
|---|---|---|
| 123 ÷ 2 | 61 | 1 |
| 61 ÷ 2 | 30 | 1 |
| 30 ÷ 2 | 15 | 0 |
| 15 ÷ 2 | 7 | 1 |
| 7 ÷ 2 | 3 | 1 |
| 3 ÷ 2 | 1 | 1 |
| 1 ÷ 2 | 0 | 1 |
余りを下から上へ読みます。
1111011
したがって、次のようになります。
123(10進数)=1111011(2進数)

2進数から8進数へ変換する
2進数から8進数へ変換するときは、右端から3桁ずつ区切ります。
123を表す2進数は、次のとおりです。
1111011
右から3桁ずつ区切ります。
1 111 011
左端の桁数が不足しているため、先頭へ0を補います。
001 111 011
値の先頭へ0を補っても、値そのものは変わりません。
それぞれの3桁を、8進数1桁へ変換します。
| 2進数3桁 | 8進数 |
|---|---|
| 001 | 1 |
| 111 | 7 |
| 011 | 3 |
左から並べると173になります。
1111011(2進数)=173(8進数)
C言語のソースコードで8進整数定数として書く場合は、先頭に0を付けます。
0173

2進数から16進数へ変換する
2進数から16進数へ変換するときは、右端から4桁ずつ区切ります。
1111011
4桁ずつ区切ります。
111 1011
左端に0を補います。
0111 1011
それぞれの4桁を、16進数1桁へ変換します。
| 2進数4桁 | 10進数 | 16進数 |
|---|---|---|
| 0111 | 7 | 7 |
| 1011 | 11 | B |
左から並べると7Bになります。
1111011(2進数)=7B(16進数)
C言語のソースコードで16進整数定数として書く場合は、先頭に0xを付けます。
0x7B

4ビットと16進数の対応表
2進数4桁を4ビットと呼びます。
4ビットでは0000から1111までを表せます。この16種類が、16進数の0からFまでに対応します。
| 10進数 | 2進数 | 16進数 |
|---|---|---|
| 0 | 0000 | 0 |
| 1 | 0001 | 1 |
| 2 | 0010 | 2 |
| 3 | 0011 | 3 |
| 4 | 0100 | 4 |
| 5 | 0101 | 5 |
| 6 | 0110 | 6 |
| 7 | 0111 | 7 |
| 8 | 1000 | 8 |
| 9 | 1001 | 9 |
| 10 | 1010 | A |
| 11 | 1011 | B |
| 12 | 1100 | C |
| 13 | 1101 | D |
| 14 | 1110 | E |
| 15 | 1111 | F |
最初からすべてを暗記する必要はありません。
まずは、次のような特徴的な対応から覚えると分かりやすくなります。
0000 = 0
0001 = 1
1010 = A
1011 = B
1111 = F
8ビットは16進数2桁で表せる
8ビットの2進数は、4ビットずつ2つに分けられます。
11111111
4桁ずつ区切ります。
1111 1111
1111は16進数のFなので、次のようになります。
11111111(2進数)=FF(16進数)
C言語で16進整数定数として書く場合は、0xを付けます。
0xFF
同じように、次の対応も確認できます。
00001111(2進数)=0F(16進数)
11110000(2進数)=F0(16進数)
C言語では、それぞれ次のように書けます。
0x0F
0xF0
この対応は、後でビット演算やフラグ処理を学ぶときに役立ちます。

C言語における整数定数の書き方
C17では、整数定数を10進数、8進数、16進数で記述できます。
| ソースコード上の表記 | 意味 | 10進数での値 |
|---|---|---|
| 123 | 10進整数定数 | 123 |
| 0173 | 8進整数定数 | 123 |
| 0x7B | 16進整数定数 | 123 |
| 0X7B | 16進整数定数 | 123 |
2進数については、C17の標準的な整数定数として直接記述する方法がありません。
そのため、2進数の1111011を表したい場合は、同じ値を10進数の123や16進数の0x7Bで書きます。
値と表記方法を分けて考える
次の3つの変数は、異なる表記の整数定数で初期化されています。
unsigned int decimal_value = 123;
unsigned int octal_value = 0173;
unsigned int hex_value = 0x7B;見た目は異なりますが、3つの変数へ保存される値は同じです。
| 変数 | ソースコード上の表記 | 保存される値 |
|---|---|---|
| decimal_value | 123 | 123 |
| octal_value | 0173 | 123 |
| hex_value | 0x7B | 123 |
変数の中へ、10進数、8進数、16進数という見た目が保存されるわけではありません。
進数の違いは、ソースコードへ値を書く方法や、画面へ値を表示する方法の違いです。
printfで表示する進数を選べる
printfでは、変換指定を変えることで、同じ値を10進数、8進数、16進数として表示できます。
| 変換指定 | 表示形式 | 123を表示した例 |
|---|---|---|
| %u | 符号なし10進数 | 123 |
| %o | 8進数 | 173 |
| %x | 小文字を使う16進数 | 7b |
| %X | 大文字を使う16進数 | 7B |
たとえば、同じ変数valueを異なる形式で表示できます。
printf("%u\n", value);
printf("%o\n", value);
printf("%x\n", value);
printf("%X\n", value);表示結果は次のようになります。
123
173
7b
7B
変数へ保存されている値は同じですが、printfによる見せ方が変わっています。
%oや%Xでは接頭辞が自動的に付かない
%oで8進数を表示しても、通常は先頭の0が自動的に付きません。
printf("%o\n", value);表示結果は173です。
173
%Xで16進数を表示しても、通常は0xや0Xが自動的に付きません。
printf("%X\n", value);表示結果は7Bです。
7B
ソースコード上の0173や0x7Bと、printfによる表示結果の173や7Bを区別しておきましょう。
同じ値を異なる進数で確認するプログラム
次のプログラムでは、10進数、8進数、16進数で書かれた同じ値を比較します。また、1つの値を複数の表示形式で出力します。
同じ値を10進・8進・16進で表示するプログラム
ファイル名:4_6_1.c
// 同じ値を10進・8進・16進で表示するプログラム
#include <stdio.h>
int main(void)
{
/* 同じ値を異なる進数の整数定数で表す */
unsigned int decimal_value = 123;
unsigned int octal_value = 0173;
unsigned int hex_value = 0x7B;
/* すべて10進数で表示して値を比較する */
printf("10進整数定数123の値:%u\n", decimal_value);
printf("8進整数定数0173の値:%u\n", octal_value);
printf("16進整数定数0x7Bの値:%u\n", hex_value);
/* decimal_valueを異なる形式で表示する */
printf("123を10進数で表示:%u\n", decimal_value);
printf("123を8進数で表示:%o\n", decimal_value);
printf("123を16進数の小文字で表示:%x\n", decimal_value);
printf("123を16進数の大文字で表示:%X\n", decimal_value);
return 0;
}実行結果の例
10進整数定数123の値:123
8進整数定数0173の値:123
16進整数定数0x7Bの値:123
123を10進数で表示:123
123を8進数で表示:173
123を16進数の小文字で表示:7b
123を16進数の大文字で表示:7B3つの整数定数は同じ値を表す
次の3行では、異なる表記方法を使っています。
unsigned int decimal_value = 123;
unsigned int octal_value = 0173;
unsigned int hex_value = 0x7B;decimal_valueは10進整数定数123で初期化しています。
octal_valueは8進整数定数0173で初期化しています。
hex_valueは16進整数定数0x7Bで初期化しています。
3つを%uで10進表示すると、すべて123になります。
printf("10進整数定数123の値:%u\n", decimal_value);
printf("8進整数定数0173の値:%u\n", octal_value);
printf("16進整数定数0x7Bの値:%u\n", hex_value);この結果から、表記方法が違っても、保存される値は同じであることを確認できます。
%uで符号なし10進数として表示する
このプログラムでは、変数をunsigned int型として宣言しています。
unsigned int decimal_value = 123;unsigned int型の値を10進数で表示するため、%uを使用しています。
printf("%u\n", decimal_value);実行結果は123です。
%oで8進数として表示する
decimal_valueを%oで表示すると、8進数の173になります。
printf("%o\n", decimal_value);実行結果は次のとおりです。
173ソースコードへ8進整数定数を書くときは0173ですが、%oによる通常の表示では173になります。
%xと%Xで16進数として表示する
%xを使用すると、16進数のaからfが小文字で表示されます。
printf("%x\n", decimal_value);実行結果は次のとおりです。
7b%Xを使用すると、AからFが大文字で表示されます。
printf("%X\n", decimal_value);実行結果は次のとおりです。
7B値そのものは同じなので、読みやすさや表示目的に合わせて%xと%Xを選べます。
図3:C言語での整数表記とprintfの表示形式

この図から分かること
ソースコードでは、接頭辞によって整数定数の進数を指定します。
- 接頭辞なしは10進整数定数
- 先頭の0は8進整数定数
- 先頭の0xまたは0Xは16進整数定数
printfでは、変換指定によって表示形式を選びます。
- %uは符号なし10進数
- %oは8進数
- %xは小文字の16進数
- %Xは大文字の16進数
値の書き方と表示方法は別に考えることが大切です。
先頭の0を見落とさない
C言語で特に間違えやすいのが、8進整数定数の先頭に付く0です。
int number = 0123;この0123は、10進数の123ではありません。8進数の123なので、10進数では83です。
10進数の123を保存したい場合は、先頭に0を付けません。
int number = 123;コードを読むときは、整数の先頭に0が付いていないか確認しましょう。
16進数のAからFを文字と混同しない
16進数に使われるAからFは、英字のように見えますが、16進数では数字の一部です。
A=10
B=11
C=12
D=13
E=14
F=15
たとえば、0x1FのFは文字定数ではありません。16進数の15を表す数字です。
1 × 16 + 15
= 31
したがって、0x1Fは10進数の31です。
2進数を4桁ずつ見る習慣を付ける
2進数が長くなると、1桁ずつ読むのは大変です。
次のように4桁ずつ区切ると、16進数へ置き換えやすくなります。
1101 1010
対応表を見ると、次のようになります。
1101 → D
1010 → A
したがって、次の対応になります。
11011010(2進数)=DA(16進数)
C言語で16進整数定数として書く場合は、次のようになります。
0xDA
小さい値で変換を練習しよう
進数の変換は、小さな値から練習すると理解しやすくなります。
| 10進数 | 2進数 | 8進数 | 16進数 |
|---|---|---|---|
| 5 | 101 | 5 | 5 |
| 8 | 1000 | 10 | 8 |
| 10 | 1010 | 12 | A |
| 15 | 1111 | 17 | F |
| 16 | 10000 | 20 | 10 |
| 31 | 11111 | 37 | 1F |
| 123 | 1111011 | 173 | 7B |
| 255 | 11111111 | 377 | FF |
特に次の対応は、ビット演算を学ぶときにもよく登場します。
15(10進数)=1111(2進数)=17(8進数)=F(16進数)
31(10進数)=11111(2進数)=37(8進数)=1F(16進数)
255(10進数)=11111111(2進数)=377(8進数)=FF(16進数)
進数のすべてを一度に暗記する必要はありません。
まずは、2進数3桁と8進数1桁、2進数4桁と16進数1桁の対応を意識してみましょう。同じ値を複数の表記で書き、printfの%u、%o、%x、%Xで表示して確かめると、数字同士のつながりが少しずつ見えるようになります。
