C言語編 第19章 整数型

先頭へ戻る

この章の概要

この章の概要です。

整数型

ここまでの章では、整数を扱うときには必ず int型を使いました。しかし、整数を扱うことができる型は int型以外にも多数存在しています。

整数を扱う型をまとめて、整数型と呼びます。この章で、大半の整数型を紹介します。なお、すべての型の一覧表が、「APPENDIX 型の分類表」にあります。

この章で取り上げていないのは、列挙型(第50章)と、_Bool型(第13章)、拡張符号付き整数型、拡張符号無し整数型です。列挙型以外は C99 で追加されたものです。

この章で取り上げる多数の整数型の違いは、型の大きさと、符号の有無の2点です。

型の大きさとは、ある型の値を表現するために何バイト必要かということです。int型は中間的な大きさを持っているといえます。使えるメモリが少ないなどの理由で、より小さい整数型を使ったり、巨大な値を表現したいなどの理由で、より大きい整数型を使ったりします。

符号の有無とは、端的にいえば、負の数を表現できるかどうかということです。int型は表現できます。あえて、負の数を表現できない整数型を使うことで、代わりに表現できる正の数を増やすことができます。また、データの意味的に負の数になり得ない場合(年齢とか)に、それを明確にする効果もあります。

sizeof演算子

まずは、整数型の大きさについての話をします。

整数型の大きさを理解することは、その型で表現できる値の範囲を知ることにつながります。型の大きさが何バイト(何ビット)なのかが分かれば、前の章で得た知識を使って、表現できる値の範囲も分かります(もっと直接的に知る方法はありますが、これは後で取り上げます)。

型の大きさは、ここまでの章でも何度か使っている sizeof演算子で知ることができます。今まで深入りせずに使ってきたので、ここで詳細を説明しておきます。

sizeof演算子は、以下のいずれかの構文で使用します。

sizeof(型名)
sizeof(式)
sizeof

型に応じた大きさ、あるいは式の結果の型の大きさがバイト単位で得られます。型名を指定する場合は ( ) で囲まないといけません。式の場合でも、( ) で囲む方が一般的かと思います。具体的なコードは次のようになります。

size_t a;
a = sizeof(int);

int num = 0;
a = sizeof(num);
a = sizeof num;    /* 上の行と同じ意味 */

sizeof演算子に与える式の部分は実行はされません。例えば、「sizeof(func())」のように使用したとしても、func関数が呼び出されることはありません。この場合、func関数の戻り値の型に応じて、その大きさが得られます(そのため、func関数の戻り値型が void の場合、コンパイルエラーになります)。

式を実行して値を得ることを、式を評価するといいます。詳細は第27章で解説します。

C99 で追加された可変長配列(第25章)の場合だけは例外で、式が評価されます。

sizeof演算子で得られる値は、size_t型の定数です。要は、コンパイルした時点で、何らかの整数の値が得られるということです。

size_t型は、標準ライブラリの中で定義されているため、使用するためには #include で取り込む必要があります。stddef.h をはじめとして、幾つかの標準ヘッダで定義されています。stdio.h でも定義されているため、大抵はあまり意識せずに使用できます。

size_t型も整数を扱う型ですが、通常、型の分類表のものには登場しません。size_t型は、C言語に組み込まれている標準的な型ではなく、typedef(第26章)という機能によって作られた、型の別名に過ぎないからです。

int型の大きさを出力してみましょう。

#include <stdio.h>

int main(void)
{
    printf( "int型の大きさは %dByte\n", sizeof(int) );

    return 0;
}

実行結果:

int型の大きさは 4Byte

コンパイルすると、printf関数のところで警告が出るかも知れませんが、とりあえず無視して構いません。

size_t型は、符号無し整数型(後述)であるためです。printf関数の変換指定子 "%d" は、符号付き整数型の値を出力するために用いるものであるため、一致していないことを警告されます。符号無し整数型を出力する場合は "%u" を使います。なお、C99 以降であれば、size_t型専用に "%zu" という指定が追加されているので、こちらを使うべきです(後述)。

int型の大きさが 4バイトだという結果が出ましたが、使っているコンパイラによって異なる可能性があります。

C99 (printf関数、scanf関数での size_t型の扱い)

C99 では、size_t型を printf関数や scanf関数で使用する際には、"%zu" という変換指定を使います。"%zu" は size_t型を扱うための専用の変換指定です。

#include <stdio.h>

int main(void)
{
    printf( "int型の大きさは %zuByte\n", sizeof(int) );

    return 0;
}

実行結果:

int型の大きさは 4Byte

int型の大きさと表現範囲

先ほどのサンプルプログラムでは、int型の大きさは 4バイトになりましたが、整数型の大きさは明確に定められてはおらず、使っているコンパイラによって異なる可能性があります

int型の大きさに関して、明確に決められていることは、16ビット以上の大きさであるという点だけです。先ほどは 4バイトという結果だったので、32ビットであると考えられます。

仮に 32ビットであるとすると、表現できる値の範囲はどのくらいになるでしょうか? 前の章の内容を踏まえて考えてみましょう。

まず、int型は負数も扱えます。負数の表現には、2 の補数表現を使うと仮定すると(この仮定はほとんど確実に正しいです)、最上位ビットは符号の表現に使わなければなりませんから、231 で表現できる数が最大値になります。計算すると、最大値は +2,147,483,647 であることが分かります。

最小値の方は、2 の補数表現では、正の側よりも 1つ多く表現できるので -2,147,483,648 です。ただし、C言語では、2 の補数表現を使うことが強制されている訳ではないので、確実に表現できる最小値は -2,147,483,647 となります。

よって、32ビットの int型で確実に表現できる値の範囲は、-2,147,483,647~+2,147,483,647 です。同じように計算すると、int型の最小の大きさである 16ビットでは -32767~+32767 となります

より確実に限界値を調べる方法を、後で取り上げます。自分が使うコンパイラでは、どのような結果になるのか、一度確認しておくと良いと思います。

short と long

今度は、int型よりも小さい整数型や、大きい整数型を取り上げます。

小さい整数型は short、大きい整数型は long というキーワードを使って表します。

short int num;  /* 小さい int */
long int num;   /* 大きい int */

このように「int」の手前に、short や long といったキーワードを付け加えます。あるいは、以下のように「int」を省略しても構いません。

short num;  /* short int と同じ */
long num;   /* long int と同じ */

型の名前について、short型なのか short int型なのか、long型なのか long int型なのか、あまり一定しませんが、どちらでも同じ意味と受け取って問題ありません。

ただの int型と同じく、明確に決められていることは最小の大きさであって、具体的な大きさはコンパイラによって異なる可能性があります。

しかも、short型が int型より小さいことや、long型が int型より大きいことにすら保証がなく、「sizeof(short) == sizeof(int)」や「sizeof(int) == sizeof(long)」であることがあり得ます。

規格で定められていることは以下の表のとおりです。

最小の大きさ 備考
int 16ビット
short 16ビット int型より大きいということはない
long 32ビット int型より小さいということはない

特別、大きさを大小させることに意味がない限り、short型や long型よりも、普通の int型を優先的に使うべきです通常、int型が最も高速に処理できます。小さい方が速いと思うかも知れませんが、必ずしも、そのような期待が成り立つとは限りません。

short型を使う価値がある場面は、メモリの使用量を少なく抑えたい場合です。とはいえ、現在のコンピュータのメモリ容量を考えると、4バイトを 2バイトに抑える程度の節約には何の意味もありません。ものすごく膨大な数の変数を定義しなければならないプログラムや、マイクロコンピュータのような、非常にメモリ容量が少ない特殊な環境でもない限りは、素直に int型を使った方が無難です。

long型を使う価値がある場面は、(正負問わず)巨大な数を扱う必要がある場合です。コンパイラによっては、int型の大きさが 16ビットしかないため、この大きさで扱えないような大きな数を使いたい場合は、long型を使います。long型なら少なくとも 32ビットの大きさが保証されます。


long型の定数を記述する場合、数値の末尾に「L」か「l」を付けなければなりません。単に「100」と書くと、その型が分からないためです。

12345678L;  /* long型 */
12345678l;  /* long型 */
1234;       /* int型 */

「l」は、「1」と見間違いやすいので「L」を使う方が好まれます。上の例のように、確かに見づらいです。

なお、「L」や「l」のように、整数定数の末尾につける文字を、整数接尾語と呼びます。

short型の方には、整数接尾語がありません。この理由は現時点で説明することが困難ですが、簡単に言えば、必要性がないからです。

short型の値は、代入や計算などを行うときなどに、自動的に int型に拡張されるからです。このように、型は暗黙的に変換されることがあります。詳細は、第21章で取り上げます。


printf関数scanf関数で、short型や long型を使う際には、変換修飾子を用いる必要があります。

変換修飾子は、short型なら "h"、long型なら "l" です。これらを、"%d" と組み合わせて、例えば、"%hd" だとか "%ld" といったように指定します。

printf( "%hd\n", sn );    /* short型 */
printf( "%ld\n", ln );    /* long型 */

変換指定子と変換修飾子(更に、他のものが付くこともあります)をあわせて、変換指定と呼びます。

(C99) long long

C99

C99 で、long よりも更に大きな long long型 が追加されました。使い方は、long とまったく同じです。

long long int num;
long long num;

long long型の最小の大きさは 64ビットと定義されています。

long long型の定数を記述する際に末尾に付加する整数接尾語は、「LL」か「ll」です。小文字は「1」と見間違えやすいので「LL」が良いでしょう。

printf関数や scanf関数などで、long long型の値を使う場合は、変換修飾子 "%ll" と組み合わせます。

printf( "%lld\n", 100LL );    /* long long型 */

signed と unsigned

ここまでに取り上げた int型、short型、long型はいずれも、正の数と負の数を両方とも扱えます。このように、正負のどちらも扱える整数型を、符号付き整数型と呼びます。

符号付き整数型であることを明確に示すために、signed というキーワードが用意されています。

signed int num1;
signed short num2;
signed long num3;

しかし、signed は付けても付けなくても同じ意味になるため、普通は省略します。また、逆に signed とだけ書くことも可能で、この場合は int型のことになります。以下の2つは同じ意味です。

int num1;
signed num1;

一方、符号を覚える能力をなくして、常に正の数だけを扱うようにした整数型を、符号無し整数型といいます。符号無し整数型は、unsigned というキーワードを使って表現します。

unsigned int num1;
unsigned short num2;
unsigned long num3;

符号無し整数型の大きさは、対応する符号付き整数型と同じです。int型が 32ビットなら、unsigned int型も 32ビットです。

負数を扱う能力を捨ててまで、符号無し整数型を使う理由の1つは、表現できる正の数の範囲を増やすことです。最上位ビットを使えるようになるため、符号付き整数型よりも2倍ほど大きい値が表現できます。例えば、32ビットの符号無し整数型は、0~4,294,967,295 まで扱えます。

また、負数になり得ないことを明確に示すために、符号無し整数型を使うこともあります。例えば、年齢は負数になり得ないので、unsigned int型にするといったことが考えられます。

ただし、型の考え方を重要視するC言語では、signed と unsigned を混在して使うと、トラブルに巻き込まれることがあります。例えば、signed int型の変数に負数が格納されているとき、その値を unsigned int型の変数へ代入したら、負数ではなくなってしまいます。つまり、ある型で表現可能な値が、他の型で表現できるとは限らないのです。

一方で、プログラマには、そのような代入が確実に問題ないことが分かっていることもあります。つまり、signed int型の変数だが、確実に unsigned int型で表現可能な値しか格納していないと、自信を持っていえるケースもあり得るでしょう。そのため、このような型の混在自体は、コンパイルエラーになりません。

符号無し整数型の定数を記述する場合は、整数接尾語の「U」か「u」を付けます。long型の場合などで、ほかの整数接尾語がある場合は両方をセットで記述します。

1234U;    /* unsigned int型 */
1234UL;   /* unsigned long型 */

「UL」は「LU」になっても構いませんし、もちろん小文字でも構いません。


printf関数scanf関数で、符号無し整数型を扱う場合には、変換指定子を "%u" にします。

printf( "%hu\n", usn );    /* unsigned short型 */
printf( "%u\n", un );      /* unsigned int型 */
printf( "%lu\n", uln );    /* unsigned long型 */

"%u" は 10進数の符号無し整数型の値を扱います。8進数や 16進数を使う場合は、前章で見たとおり、"%o"、"%x"、"%X" を使います。"%o"、"%x"、"%X" は元々、符号無し整数型を扱う変換指定子です。

printf( "%o\n", un );      /* unsigned int型 (8進数で出力) */
printf( "%x\n", un );      /* unsigned int型 (16進数で出力) */
printf( "%lo\n", un );     /* unsigned long型 (8進数で出力) */
printf( "%lx\n", un );     /* unsigned long型 (16進数で出力) */


char型

char型は、1文字を表現するための型で文字型ですが、実はこれも広くいえば整数型です。

char型の大きさは必ず 1バイトです

1バイト が 8ビットとは限らないところに問題が潜んでいますが、そういう非常に特殊な環境のことは考えないことにします。limits.h をインクルードして、CHAR_BIT の値を調べれば、そのコンパイラで、char型が何ビットであるのかが分かります。

char型は、1バイトの整数型ですから、小さな整数であれば表現可能です。また、signed および unsigned を付けることが可能です。

char c1;
signed char c2;
unsigned char c3;

ただ、int型などの他の整数型と違って、char型と signed char型は異なる型ですし、unsigned char型も異なる型です。つまり、上記の3つの宣言は、すべて異なる型の変数を宣言しています。

そして、ただの char型が扱える値の範囲は、コンパイラによって異なります。可能性としては、signed char型相当の値の範囲を扱えるか、unsigned char型相当の値の範囲を扱えるかのいずれかです。

このように、char型関連の事情はかなり複雑ですが、short型ですら最小サイズが 16ビットと定められているので、1バイトの整数型が必要であれば、char型に頼るしかありません。

1バイトの整数型が必要な場合は、明示的に signed や unsigned を付けるようにして、値の範囲を明確にしましょう。ただの char型は、文字を扱うことだけに使うのが無難です。

C99 (printf関数、scanf関数での char型の扱い)

C99 では、char型を printf関数や scanf関数で使用する際には、"%hh" という変換修飾子を使うことができます。


また、文字定数の型についても注意が必要です。

#include <stdio.h>

int main(void)
{
    char a = 'A';

    printf( "%u\n", sizeof('A') );
    printf( "%u\n", sizeof(a) );

    return 0;
}

実行結果:

4
1

sizeof演算子を使って char型の大きさを調べてみると 1 になりますが、'A' のような文字定数を調べると 4 になっています。

予想に反して、文字定数の型は int型です。実験した環境では int型の大きさが 4バイトなのでこのような結果になりましたが、int型が 2バイトの環境なら、sizeof('A') も 2 になります。

C++ では、文字定数の型は char型であり、sizeof('A') の結果は常に 1 になります。これは、C言語と C++ の代表的な非互換な部分です(Modern C++編【言語解説】第2章)。

限界値

ある型が実際に扱うことができる値の範囲は、型の大きさを調べて、手計算しても分かりますが、もっと直接的に調べることができます。

次のプログラムを実行すると、各型の限界値(最小値と最大値)が分かります。

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

int main(void)
{
    printf( "          char型の最小値は %d、最大値は %d\n", CHAR_MIN, CHAR_MAX );
    printf( "   signed char型の最小値は %d、最大値は %d\n", SCHAR_MIN, SCHAR_MAX );
    printf( " unsigned char型の最小値は %u、最大値は %u\n", 0, UCHAR_MAX );
    printf( "\n" );
    printf( "         short型の最小値は %d、最大値は %d\n", SHRT_MIN, SHRT_MAX );
    printf( "unsigned short型の最小値は %u、最大値は %u\n", 0, USHRT_MAX );
    printf( "\n" );
    printf( "           int型の最小値は %d、最大値は %d\n", INT_MIN, INT_MAX );
    printf( "  unsigned int型の最小値は %u、最大値は %u\n", 0, UINT_MAX );
    printf( "\n" );
    printf( "          long型の最小値は %ld、最大値は %ld\n", LONG_MIN, LONG_MAX );
    printf( " unsigned long型の最小値は %lu、最大値は %lu\n", 0, ULONG_MAX );

    return 0;
}

実行結果:

          char型の最小値は -128、最大値は 127
   signed char型の最小値は -128、最大値は 127
 unsigned char型の最小値は 0、最大値は 255

         short型の最小値は -32768、最大値は 32767
unsigned short型の最小値は 0、最大値は 65535

           int型の最小値は -2147483648、最大値は 2147483647
  unsigned int型の最小値は 0、最大値は 4294967295

          long型の最小値は -2147483648、最大値は 2147483647
 unsigned long型の最小値は 0、最大値は 4294967295

出力結果は、使用しているコンパイラによって異なります。

整数型に関する情報を得るためには、limits.h を #include すると使えるようになる機能を使用します。

以下の表にまとめました。unsigned な型の場合は、最大値の方だけが用意されています。最小値は必ず 0 です。

最小値 最大値
char CHAR_MIN CHAR_MAX
signed char SCHAR_MIN SCHAR_MAX
unsigned char 0 UCHAR_MAX
short SHRT_MIN SHRT_MAX
unsigned short 0 USHRT_MAX
int INT_MIN INT_MAX
unsigned int 0 UINT_MAX
long LONG_MIN LONG_MAX
unsigned long 0 ULONG_MAX
(C99) long long LLONG_MIN LLONG_MAX
(C99) unsigned long long 0 ULLONG_MAX

限界値を超える場合の結果

ところで、最大値の次( +1 )はいくつになるのでしょう? 次のプログラムで試してみましょう。

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

int main(void)
{
    short int num = SHRT_MAX + 1;
    unsigned short int unum = USHRT_MAX + 1;

    printf( "num = %d\n", num );
    printf( "unum = %d\n", unum );

    return 0;
}

実行結果:

num = -32768
unum = 0

警告が出るかも知れませんが、ここでは無視して下さい。重要なのは実行結果です。

まず、符号無し整数型の方ですが、0 になりました。これはつまり、最小値に戻ってきているということですが、正確にいえば、表現できる最大値 + 1 で割った余りになっています

符号付き整数型の場合の結果もやはり、最小値に戻ってきていると捉えることができそうですが、これは保証されたものではありません。符号付き整数型で、表現できる限界値を上回る、または下回る場合の結果はコンパイラによって異なります。

最小値より小さくなったときの考え方も同様です。符号無し整数型であれば、最大値の方に戻ってきます。符号付き整数型での結果はコンパイラの実装依存です。

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

int main(void)
{
    short int num = SHRT_MIN - 1;
    unsigned short int unum = 0 - 1;

    printf( "num = %d\n", num );
    printf( "unum = %d\n", unum );

    return 0;
}

実行結果:

num = 32767
unum = 65535


練習問題

問題① short型、int型、long型の大きさを確認するプログラムを作成して下さい。

問題② char型が符号付きか、符号無しかを判定するプログラムを作成して下さい。


解答ページはこちら

参考リンク



更新履歴

'2018/7/10 「限界値」の項に、一覧表を追加。

'2018/6/7 「整数型」の項を追加。
全体的に構成と内容を修正。

'2018/6/5 第20章から整数型に関係する内容を移動してくる形で、新規作成。



前の章へ(第18章 数の表現方法)

次の章へ(第20章 浮動小数点型)

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

Programming Place Plus のトップページへ


このエントリーをはてなブックマークに追加
rss1.0 取得ボタン RSS