先頭へ戻る

整数の表現方法 | Programming Place Plus C言語編 第18章

Programming Place Plus トップページC言語編

先頭へ戻る

このページの概要

以下は目次です。


8進数

プログラミングでは、普段の生活で使っている 10進数のほか、2進数や 8進数、16進数を使う機会があります。C言語では、8進数と 16進数を特別な表記で表現できるようになっています。2進数を直接的に表記する方法はありませんが、考え方としてはよく必要になります。

もし、2進数、8進数、10進数、16進数といった数の表現に馴染みがない場合は先に、コンピュータサイエンス編>「基数」のページをお読みください。

C言語では、整数定数の先頭に 0 を付加すると、その整数を 8進数とみなします。たとえば、0100 は 8進数の 100 のことです。8進数なので、0~7 の範囲の数字しか使えませんから、0888 のような整数定数の記述はコンパイルエラーになります。

このような、先頭に付け足すことで何かしらの意味を持つ表記のことをプリフィックス(接頭辞) (prefix) といいます。

浮動小数点数を 8進法で表記することはできません。

この表記方法があることを知らなかったり忘れていたりして、桁数合わせのつもりで余計な 0 を付けてしまうミスはありがちなので注意してください。

#include <stdio.h>

int main(void)
{
    int v1 = 32554;
    int v2 = 61489;
    int v3 = 07012;  // 5桁に揃えようとして、ここだけ 8進数になってしまった
    int v4 = 22500;

    printf("%d\n", v1);
    printf("%d\n", v2);
    printf("%d\n", v3);
    printf("%d\n", v4);
}

実行結果:

32554
61489
3594
22500

v3 の出力結果だけおかしくなっています。07012 は 8進数であり、10進数に変換すると 3594 です。ある整数が、ソースコード上で何進数で表記されているかと、printf関数の変換指定子が対応するわけではありません。8進数の整数定数でも、%d 変換指定子で出力すれば 10進数に変換された結果が出力されます。

このことからわかるように、070123594 はまったく同じ数を、別の方法で表記したに過ぎません。

#include <stdio.h>

int main(void)
{
    if (07012 == 3594) {
        puts("equal");
    }
}

実行結果:

equal

ある n進数が m進数でいくつなのか確認するには、Windows の電卓などを使うと簡単で確実です。Windows編>「電卓で基数変換する」を参照してください。

printf関数

整数を 8進数の表記で出力したいときは、printf関数の %o 変換指定子を使います。ただし、%o 変換指定子では負数を扱えません

#include <stdio.h>

int main(void)
{
    printf("%o\n", 07012);
    printf("%d\n", 07012);
    printf("%o\n", -07012);  // 負数は正しく出力できない
}

実行結果:

7012
3594
37777770766

%o 変換指定子で負数が出力できないのは、%o 変換指定子は指定された整数を、後述する unsigned int型とみなすからです1

整数定数の記述では、先頭に 0 を付加する一方で、printf関数の出力結果には 0 が付加されていません。もし記述を統一してほしければ、“%#o” のように # を挟んで使用します2

#include <stdio.h>

int main(void)
{
    printf("%#o\n", 07012);
}

実行結果:

07012

sscanf関数

sscanf関数でも 8進数の整数を “%o” 変換指定子で扱えます。やはり負数は使えません。

#include <stdio.h>

int main(void)
{
    puts("Please enter the integer.");
    char input_string[20];
    fgets(input_string, sizeof(input_string), stdin);
    int value;
    sscanf(input_string, "%o", &value);  // 8進数として value へ取り出す

    printf("%o\n", value);
    printf("%d\n", value);
}

実行結果:

Please enter the integer.
1234  <-- 入力された内容
1234
668

実際には、%o 変換指定子を使う場合に、結果を受け取る変数の型が int型であることは正確には正しくなく、コンパイラは警告を出しているかもしれません。正しくは unsigned int という型を使うべきといえますが3この型に関する話題はあとで改めて取り上げることにします

16進数

C言語では、整数定数の先頭に 0x または 0X を付加すると、その整数を 16進数とみなします。たとえば、0x100 は 16進数の 100 のことです。16進数なので、0~9 の範囲の数字と a~f の範囲のアルファベットが使えます。アルファベットは小文字でも大文字でも構いません。

浮動小数点数を 16進法で表記することができます。第20章で改めて取り上げることにします。

#include <stdio.h>

int main(void)
{
    printf("%d\n", 0x100);
    printf("%d\n", 0Xabcd);
}

実行結果:

256
43981

printf関数

整数を 16進数の表記で出力したいときは、printf関数の %x 変換指定子、または %X 変換指定子を使います。“%x” の場合は「A~F」を小文字で出力し、“%X” の場合は大文字で出力します。これらの変換指定子は、負数を扱えません

#include <stdio.h>

int main(void)
{
    printf("%x\n", 0x100);
    printf("%X\n", 0xabcd);
    printf("%x\n", 0Xabcd);
    printf("%x\n", -0x100);  // 負数は正しく出力できない
}

実行結果:

100
ABCD
abcd
ffffff00

負数が出力できないのは、%x 変換指定子や %X 変換指定子は、指定された整数を、後述する unsigned int型とみなすからです4

整数定数の記述では、先頭に 0x0X を付加する一方で、printf関数の出力結果には 0x0X が付加されていません。もし記述を統一してほしければ、“%#x” や “%#X”のように # を挟んで使用します2

#include <stdio.h>

int main(void)
{
    printf("%#x\n", 0xabcd);
    printf("%#X\n", 0xabcd);
}

実行結果:

0xabcd
0XABCD

sscanf関数

sscanf関数でも 16進数の整数を “%x” 変換指定子や “%X” 変換指定子で扱えます。こちらは2つの変換指定子に違いはありません。やはり負数は使えません。

#include <stdio.h>

int main(void)
{
    puts("Please enter the integer.");
    char input_string[20];
    fgets(input_string, sizeof(input_string), stdin);
    int value;
    sscanf(input_string, "%x", &value);  // 16進数として value へ取り出す

    printf("%x\n", value);
    printf("%d\n", value);
}

実行結果:

Please enter the integer.
3c7f  <-- 入力された内容
3c7f
15487

実際には、%x 変換指定子や “%X” 変換指定子を使う場合に、結果を受け取る変数の型が int型であることは正確には正しくなく、コンパイラは警告を出しているかもしれません。正しくは unsigned int という型を使うべきといえますが3この型に関する話題はあとで改めて取り上げることにします

符号付き整数と符号無し整数

%o や %x の変換指定子が負数を使えない事情は、printf関数や sscanf関数はこれらの変換指定子を使うときに、実引数を int型ではなく、unsigned int型とみなしているためです。unsigned int型は、int型の符号無しバージョンといえる型です。

これまで使ってきた int型は整数を扱うための型であり、整数型 (integer type) と呼ばれる型に分類されています。整数型はさらに細かく細分化することができ、通常の int型は、符号付き整数型 (signed integer type) に分類できます。符号付き整数型は、「符号付き」といっているように、符号がある整数型ということで、正の数でも負の数でも(そして 0 も)表現できます

一方、unsigned int型は、符号無し整数型 (unsigned integer type) に分類されます。符号無し整数型は、符号というものを考えないことにした整数型という意味で、正の数(と 0)だけを表現できます

符号無し整数型は、負の数を表現しない代わりに、正の数の表現範囲を増やすことができるため、上限値は、同じ大きさの符号付き整数型の約2倍になります。ただし、上限値を増やしたいという理由で、符号無し整数型を使うのはやめましょう。むやみに使うと、符号付き整数型と符号無し整数型が混在したプログラムになりますが、その場合のルールはかなり複雑であり、可能なかぎり避けたほうが安全であるといえます。

第19章で紹介しますが、表現できる値の範囲を広げたいのであれば、int型よりも表現範囲が大きい符号付き整数型を使った方がいいです。たとえば、long long int という型を使うと、どんな処理系でも、少なくとも 9,223,372,036,854,775,807 まで表現できます。

型の混在に関する話題は第21章で取り上げます。

「unsigned」(アンサインド)は、「符号無し」を意味するキーワードで、「unsigned int」は「符号無し版の int型」を意味しています。ちなみに、「符号付き」は「signed」(サインド)と表現されます。

“unsigned int” は少し長い名前ですし、あいだに空白も入っていて変な感じがしますが、これで1つの型名になるので、int と同じ感覚で記述できます。やや分かりづらい表現になりますが、int を省略して unsigned とだけ書いても同じ意味とみなされます。

unsigned int value = 123;
unsigned value = 123;

ここで与えている 123 は int型なので(第10章)、unsigned int型の変数の初期値として使うことは、型が一致していないことになります。これは別に問題ないですが、整数定数の末尾に uU を付加することで、型が符号無し整数型に強制できます

unsigned int value = 123u;
unsigned int value = 123U;  // 同じ意味

このような、末尾に付け足すことで何かしらの意味を持つ表記のことをサフィックス(接尾辞) (suffix) といいます。

8進数であることを表す 0 や、16進数を表す 0x0X を付加した整数定数は、int型で表現できる範囲の整数であれば int型とみなされますが、より大きな正の整数の場合には、unsigned int型とみなされます。0123u とか 0x0123u といったように、uサフィックスと同時に使用した場合は、unsigned int型になります5


printf関数で符号無し整数型を出力するには、%u 変換指定子を使います。

#include <stdio.h>

int main(void)
{
    unsigned int value = 123u;

    printf("%u\n", value);
}

実行結果:

123

sscanf関数でも %u 変換指定子を使って、符号無し整数型を受け取れます。

#include <stdio.h>

int main(void)
{
    puts("Please enter the integer.");
    char input_string[20];
    fgets(input_string, sizeof(input_string), stdin);
    unsigned int value;
    sscanf(input_string, "%u", &value);

    printf("%u\n", value);
}

実行結果:

Please enter the integer.
123  <-- 入力された内容
123


練習問題

問題① 標準入力から 16進数の整数を受け取り、それを 8進数で出力するプログラムを作成してください。

問題② 8・10・16進数の対応表を表示するプログラムを作ってください。値の範囲は、10進数で「0~16」とします。

問題③ 次の式を手作業で計算してみてください。そして、答えを確認するためのプログラムを作成してください。
「0x3c5 × 26 - 0741」は 10進数でいくつ?

問題④ 2進数の 0111101 を 10進数と 16進数に変換してください。


解答ページはこちら

参考リンク


更新履歴

’2018/6/6 全体的に構成と内容を修正。
2進数の負の整数」の項を追加。
参考書籍を追加。

’2018/6/5 第19章から移動してくる形で新規作成。



前の章へ (第17章 処理の流れを制御する)

次の章へ (第19章 整数型)

C言語編のトップページへ

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
Twitter でツイート Twitter をフォロー LINE で送る
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー