先頭へ戻る

整数型 | Programming Place Plus C言語編 第19章

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

先頭へ戻る

この章の概要

この章の概要です。


整数型

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

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

この章で取り上げていないのは、列挙型(第50章)と、_Bool型(第13章)、拡張符号付き整数型、拡張符号無し整数型です。

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

型の大きさとは、その型が何バイトあるかということです。バイト数が多いほど、多くの情報を表現できます。整数型であれば、バイト数が多いほど、巨大な整数を表現可能です。int型は、数ある整数型の中では中間的な大きさをもっており、ほとんどの場面で十分な大きさです。

ほかの整数型は、int型よりも小さかったり、大きかったりするので、使用できるメモリが少ないといった理由があれば、より小さい整数型を使うという選択ができますし、int型で表現できないような巨大な整数を表現したいときには、より大きい整数型を使います。

一方、符号の有無とは、負の数を表現できるかどうかということです。符号がある型では負の数も表現できますが、符号がない型では正の数しか表現できません。負の数を表現できない代わりに、正の数をより多く表現できます。int型は、符号がある整数型です。

あえて、負の数を表現できない整数型を使うことで、その型で扱っているデータが負の数になりえないもの(年齢など)であることを明確にする効果があります。また、賛否はありますが、あつかえる正の数を多くする目的で使うこともあります。


short、long、long long

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

小さい整数型は short、大きい整数型は long、さらに大きい整数型は long long で表します。

short int 変数名;       // 小さい int
long int 変数名;        // 大きい int
long long int 変数名;   // さらに大きい int

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

short 変数名;       // short int と同じ
long 変数名;        // long int と同じ
long long 変数名;   // long long int と同じ

int型と同じく、明確に決められていることは最小の大きさであって、具体的な大きさは処理系によって異なります。

また、それぞれの大きさが「同じ」なこともあり得ます。たとえば「sizeof(short) == sizeof(int)」や「sizeof(int) == sizeof(long)」かもしれません。

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

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

特別、大きさを変える必要性がないかぎり、普通の int型を使うべきです。通常、int型が最も高速に処理できます。小さい方が速いと思うかもしれませんが、必ずしも、そのような期待が成り立つとは限りません。

【上級】本当に最速の型を使いたい場合、標準ライブラリに定義されている int_fast32_tのような型を検討してください。

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

long型や long long型を使う価値がある場面は、巨大な数(負の方向も含めて)を扱う必要がある場合です。処理系によっては、int型の大きさが 16ビットしかないため、この大きさで扱えないような大きな数を使いたい場合は、long型や long long型を使います。

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

1234;          // int型
12345678L;     // long型
12345678l;     // long型
1234567890LL;  // long long型
1234567890ll;  // long long型

「l」は、「1」と見間違いやすいので「L」を使う方が好まれます。上の例のように、確かに見づらいです。なお、「L」や「l」のように、整数定数の末尾につける文字を、整数接尾語と呼びます。

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

【上級】short型の値は、代入や計算などを行うときなどに、自動的に int型に拡張されるためです。このように、型は暗黙的に変換されることがあります。安易に int型以外の型を使わないことを勧めるのはこのためです。詳細は、第21章で取り上げます。

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

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

#include <stdio.h>

int main(void)
{
    short sn = 1234;
    long ln = 12345678L;
    long long lln = 1234567890LL;

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

    return 0;
}

実行結果:

1234
12345678
1234567890

変換指定子と変換修飾子(ほかに何か付くこともあります)をあわせて、変換指定と呼びます。

signed と unsigned

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

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

signed short 変数名;
signed int 変数名;
signed long 変数名;
signed long long 変数名;

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

int num;
signed num;

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

unsigned short 変数名;
unsigned int 変数名;
unsigned long 変数名;
unsigned long long 変数名;

unsigned short、unsigned int、unsigned long、unsigned long long に、後で取り上げる unsigned char型と、_Bool型を加えたものを、標準符号無し整数型と呼びます。

また、標準符号付き整数型と標準符号無し整数型を合わせて、標準整数型と呼びます。

符号無し整数型の大きさは、対応する符号付き整数型と同じです。たとえば、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" にします。

#include <stdio.h>

int main(void)
{
    unsigned short usn = 100U;
    unsigned int un = 100U;
    unsigned long uln = 1000000L;

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

    return 0;
}

実行結果:

100
100
1000000

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

#include <stdio.h>

int main(void)
{
    unsigned int un = 1000U;

    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進数で出力) 

    return 0;
}

実行結果:

1750
3e8
1750
3e8

char型

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

【上級】char型の大きさは必ず 1バイトです。1バイト が 8ビットとは限らないところに問題が潜んでいますが、そういう非常に特殊な環境のことは考えないことにします。<limits.h> をインクルードして、CHAR_BIT の値を調べれば、その処理系の char型が何ビットであるのかが分かります。

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

char 変数名;
signed char 変数名;
unsigned char 変数名;

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

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

文字型ではなく、小さな整数型が欲しいという場合は、明示的に signed や unsigned を付けるようにして、値の範囲を明確にしましょう。ただの char型は、文字を扱うことだけに使うのが適切です。

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

#include <stdio.h>

int main(void)
{
    signed char sc = -5;
    unsigned char uc = 5;

    printf( "%hhd\n", sc );
    printf( "%hhu\n", uc );

    return 0;
}

実行結果:

-5
5

文字定数の型については、注意が必要です。次のサンプルプログラムをみてください。

#include <stdio.h>

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

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

    return 0;
}

実行結果:

1
4

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

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

【上級】C++ では、文字定数の型は char型であり、sizeof('A') の結果はつねに 1 です。これは、C言語と C++ の代表的な非互換な部分です(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", 0U, UINT_MAX );
    printf( "\n" );
    printf( "long型の最小値は %ld、最大値は %ld\n", LONG_MIN, LONG_MAX );
    printf( "unsigned long型の最小値は %lu、最大値は %lu\n", 0UL, ULONG_MAX );
    printf( "\n" );
    printf( "long long型の最小値は %lld、最大値は %lld\n", LLONG_MIN, LLONG_MAX );
    printf( "unsigned long long型の最小値は %llu、最大値は %llu\n", 0ULL, ULLONG_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

long long型の最小値は -9223372036854775808、最大値は 9223372036854775807
unsigned long long型の最小値は 0、最大値は 18446744073709551615

出力結果は処理系によって異なります。

整数型に関する情報を得るためには、<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
long long LLONG_MIN LLONG_MAX
unsigned long long 0 ULLONG_MAX

C99規格からは、size_t型の最大値を SIZE_MAX で得られます。これは <stdint.h> を #include すると使えるようになります。size_t型は符号無し整数型なので、最小値は 0 です。


練習問題

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

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


解答ページはこちら

参考リンク


更新履歴

≪さらに古い更新履歴を展開する≫



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

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

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

Programming Place Plus のトップページへ



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