C言語

C言語:整数の大きさのまとめ

C言語の整数の大きさ(整数型が表現できる値の範囲)について、規格として定めていること、実際に処理系が採用している大きさ、このブログでの方針についてまとめました。

規格としての整数型の範囲

C言語の言語仕様については、ISO/IEC 9899 で定められています。C言語は、広い範囲の計算機での利用も視野に入れており、整数の大きさを規格として厳密に定義しませんでした。

規格のバージョン

ISO/IEC 9899 は、何回か改訂されています。バージョンについてまとめます。

  • C90 1990年に発行されたため、「C90」と呼ばれています。ISO規格がベースとした ANSI規格が1989年に発行されたため、「C89」とも呼ばれることがありますが、「C89」と「C90」は言語仕様として差がありません。多くの人が C言語と認識している多くの仕様がこのバージョンの仕様に基づいていると思われます。
  • C99 1999年に発行されました。これより後に続く規格と比較すると大きく変わりました。整数については、long long int と stdint.h がこのバージョンから導入されました。
  • C11 2011年に発行されました。いくつかの仕様が追加されましたが、整数の大きさについては影響がなかったと考えています。
  • C17 2018年に発行されました。2017年に規格発行の準備を予定していたため「C17」と呼ばれています。発行年に合わせて「C18」と呼ぶ場合があるかもしれません。C11 の仕様欠陥の修正が主だと認識されています。

動作しているコンパイラがどのバージョンで動作しているかは、マクロ定数 __STDC_VERSION__ として定義されている long int の値を見れば分かります。

バージョン__STDC_VERSION__ の値
C89/C90マクロが定義されていない。
C99199901L
C11201112L
C17/C18201710L

規格として定まっていること

規格としては、以下しか決まっていません。なお、long long int については、C99 からの導入となります。

  • 整数として表現できる範囲の大きさは以下の関係になります。
    singed char ≦ short int ≦ int ≦ long int ≦ long long int
  • singed char の大きさは8ビット以上
  • short int の大きさは16ビット以上
  • int の大きさは16ビット以上
  • long int の大きさは32ビット以上
  • long long int の大きさは64ビット以上
  • 上記はすべて符号付き、signed がつけば符号付き、unsigned がつけば符号無しになります。注意としては、規格として char を符号付きか符号無しか規定していません(単なる char が符号無しになる処理系がありえます)。

極端な話、char も short も int も long も long long もすべて64ビットの規格準拠のコンパイラが(規格上は)ありえます。

limits.h の規定

規格としては、整数の大きさを厳密に決めていませんが、コンパイラとして何らかの大きさを採用することになります。コンパイラが採用した大きさは、標準ヘッダファイル limits.h でマクロ定数式として定義された値により分かります。

定数名意味
SCHAR_MIN型signed charのオブジェクトにおける最小値
SCHAR_MAX型signed charのオブジェクトにおける最大値
UCHAR_MAX型unsigned charのオブジェクトにおける最大値
CHAR_MIN型charのオブジェクトにおける最小値
CHAR_MAX型charのオブジェクトにおける最大値
SHRT_MIN型short intのオブジェクトにおける最小値
SHRT_MAX型short intのオブジェクトにおける最大値
USHRT_MAX型unsigned short intのオブジェクトにおける最大値
INT_MIN型intのオブジェクトにおける最小値
INT_MAX型intのオブジェクトにおける最大値
UINT_MAX型unsigned intのオブジェクトにおける最大値
LONG_MIN型long intのオブジェクトにおける最小値
LONG_MAX型long intのオブジェクトにおける最大値
ULONG_MAX型unsigned long intのオブジェクトにおける最大値
LLONG_MIN型long long intのオブジェクトにおける最小値(C99から)
LLONG_MAX型long long intのオブジェクトにおける最大値(C99から)
ULLONG_MAX型unsigned long long intのオブジェクトにおける最大値(C99から)

stdint.h の規定(C99から)

C言語の int は、(その計算機環境に最適化することを意図して)規格としては定めていませんが、それではプログラムが書きにくい場合があります。そこで、サイズを決めた整数型を導入しました。この新しい型は、stdint.h をインクルードすることによって使うことができます。
型名の $N$ には、16や32などの具体的な数字が入ります。

型名意味
int$N$_tちょうど$N$ビットの大きさを持つ符号付き整数
例)$N$ = 16であれば、-32768から32767の値がちょうど表現できる。
どの$N$に対して、型を用意するかは、処理系依存となる。
uint$N$_tちょうど$N$ビットの大きさを持つ符号無し整数
例)$N$ = 16であれば、0から65535の値がちょうど表現できる。
どの$N$に対して、型を用意するかは、処理系依存となる。
int_least$N$_t少なくとも$N$ビットの大きさを持つ符号付き整数
C99では、$N$ = 8、16、32、64 について型を定義する必要がある。
他の$N$に対しては、処理系依存となる。
uint_least$N$_t少なくとも$N$ビットの大きさを持つ符号無し整数
C99では、$N$ = 8、16、32、64 について型を定義する必要がある。
他の$N$に対しては、処理系依存となる。
int_fast$N$_t少なくとも$N$ビットの大きさを持ち、最速で動作する符号付き整数
C99では、$N$ = 8、16、32、64 について型を定義する必要がある。
他の$N$に対しては、処理系依存となる。
uint_fast$N$_t少なくとも$N$ビットの大きさを持ち、最速で動作する符号無し整数
C99では、$N$ = 8、16、32、64 について型を定義する必要がある。
他の$N$に対しては、処理系依存となる。

多倍長整数

C の規格としては、大きな値が扱える多倍長整数を定めていません。多倍長演算が利用できるライブラリとして、The GNU Multiple Precision Arithmetic Library(GMP)があります。GMP は別途紹介することにします。

多倍長整数を使いたい場合は、Python や Ruby では無限長の整数が使えるため、これらの言語を使う方が良いかもしれません。

実際に処理系が採用している大きさ

規格としては、整数の大きさは決まっていませんが、現実的には、以下の実装が多かったという印象です。

  • char は、8ビット
    符号付き:-128~127
    符号無し:0~255
  • short int は、16ビット
    符号付き:-32768~32767
    符号無し:0~65535
  • int は、32ビット
    符号付き:-2147483648~2147483647
    符号無し:0~4294967295
    ただし、組込み向けで昔は、16ビットもあった。
  • long int は、32ビット(int と同じ)
    符号付き:-2147483648~2147483647
    符号無し:0~4294967295
  • long long int は、64ビット
    符号付き:ー9223372036854775808~9223372036854775807
    符号無し:0~18446744073709551615

32ビットの大きさは、だいだいプラスマイナス20億(符号無しの場合は40億)、64ビットの大きさは、だいだい19桁(符号無しの場合は20桁)と概算していました。

コンパイラ環境が printf 出力できるなら次のプログラムで、コンパイラが採用している整数の大きさが分かります。

#include <stdio.h>

int main(void)
{
	printf("signed char  : %d\n", (int)sizeof(signed char));
	printf("short int    : %d\n", (int)sizeof(short int));
	printf("int          : %d\n", (int)sizeof(int));
	printf("long int     : %d\n", (int)sizeof(long int));
	printf("long long int: %d\n", (int)sizeof(long long int));

	return 0;
}

わたくしは、Windows 環境で cygwin を使っています。その環境でこのプログラムを動作させました。結果は以下です。

  • signed char は、8ビット
  • short int は、16ビット
  • int は、32ビット
  • long int は、64ビット
  • long long int は、64ビット

コンパイラは、gcc (GCC) 11.3.0 で、特に言語バージョンは指定していません。ずっと long は、32ビットだと考えていましたが、知らない間に64ビットになっていたようです。一方、int は32ビットのままでした。

このブログの方針

このブログで紹介する C プログラムは整数に関して、次の方針で記述します。

  • 言語バージョンは、C99 を採用するが、long long int を使う以外は、可能な限り C90 から提供している機能でプログラムを実装する。
  • short int は原則使わない。
  • int を32ビットと仮定して、可能な限り int を使って書く。
    規格に従えば、このような場合は、int ではなく int_least32_t を使うべきですが、普及している型を使うことを優先しました。
  • 著者の環境では、long int が64ビットであったが、32ビットと仮定してプログラムを書く。つまり32ビットを超える整数を使うときは、long long int を使う。またブログでの記事も long int を32ビットと仮定して記述する。
  • このため、long int は使われなくなる。これは、32ビットの範囲の整数は int で書き、それを超える整数は、long long int を使うことになるため。
  • stdint.h で定義している型は使わない。

将来は、int が64ビットになるなど、この方針を変更をする時代が来るかもしれませんが、しばらくはこれでプログラミングを楽しみます。

最後に

Project Euler は、32ビットを超える整数を扱う場合があります。そのため、C の整数に関する規格と実際の処理系について紹介しました。このブログでは、読みやすさを優先してプログラムを記述しているため、採用した方針についても紹介しました。

C の整数を使いこなしていきましょう!

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA