C言語基礎|整数型の表現範囲

整数型をちゃんと使い分けるコツは、めちゃくちゃシンプルで―
その型が表せる最小値・最大値(表現範囲)を知っておくことです。

たとえば「人数」「回数」「配列の要素数」みたいに 0以上しか出ない 値なら unsigned が候補になりますし、
「差分」「増減」「温度」みたいに 負が出る 値なら signed が必要になります。

そしてもう1つ。
基本は int を使う。これがCの“気持ちいい”スタート地点です。
(必要になったら long や long long に引っ越す、が定石)

まずは表現範囲を一覧でつかもう(標準Cで保証される最低限)

下の表は「どの処理系でも最低これだけは保証するよ」という 最低ラインです。
実際の環境では、これより広いことが多いです。

整数型の表現範囲(標準Cで保証される値)

最小値最大値
char0255
char(別の可能性)-127127
signed char-127127
unsigned char0255
short-32,76732,767
int-32,76732,767
long-2,147,483,6472,147,483,647
long long-9,223,372,036,854,775,8079,223,372,036,854,775,807
unsigned short065,535
unsigned065,535
unsigned long04,294,967,295
unsigned long long018,446,744,073,709,551,615

この表の「読みどころ」

  • char は処理系依存で、符号付きにも符号無しにもなり得ます(どっちかは環境次第)
  • unsigned 系の 最小値は常に0
  • long / long long は大きい数が扱えるけど、環境によっては 計算が重くなったり メモリが増えたりします

なぜ「基本は int」なの?

ここ、超大事ポイントです。

int / long / long long の使い分けイメージ

得意なこと注意点よくある用途
int扱いやすい、速いことが多い範囲が足りないことがあるほとんどの整数計算、ループ変数
longintより範囲を広げたい環境でサイズが違う大きめの計算、時間、IDなど
long longかなり大きい整数出力書式も専用が必要大きなカウンタ、桁が大きい値

結論:迷ったら int
足りないと分かったときにだけ、型を大きくするのがラクです。

「本当の範囲」は <limits.h> で調べるのが正解

さっきの表は「最低保証」です。
でも実際には、あなたのPC/コンパイラでの範囲を知りたいですよね。

そのために C では <limits.h> が用意されています。
ここには各型の最小値・最大値が マクロで入っています。

<limits.h> にある代表的な範囲マクロ

最小値マクロ最大値マクロ
charCHAR_MINCHAR_MAX
signed charSCHAR_MINSCHAR_MAX
unsigned charなし(最小は0)UCHAR_MAX
shortSHRT_MINSHRT_MAX
intINT_MININT_MAX
longLONG_MINLONG_MAX
long longLLONG_MINLLONG_MAX
unsigned intなし(最小は0)UINT_MAX

※ unsigned の最小値は常に 0 なので、最小値マクロは基本的にありません。

サンプルプログラム

「自分の環境での範囲を表示する」プログラムを例に解説をします。

プロジェクト名:chap7-6-1 ソースファイル名:chap7-6-1.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

// 自分の環境での整数型の表現範囲を表示する

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

int main(void)
{
    puts("=== この環境での整数型の表現範囲 ===");

    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", 0U, UCHAR_MAX);

    printf("short             : %d ~ %d\n", SHRT_MIN, SHRT_MAX);
    printf("int               : %d ~ %d\n", INT_MIN, INT_MAX);
    printf("long              : %ld ~ %ld\n", LONG_MIN, LONG_MAX);
    printf("long long         : %lld ~ %lld\n", LLONG_MIN, LLONG_MAX);

    printf("unsigned short    : %u ~ %u\n", 0U, USHRT_MAX);
    printf("unsigned int      : %u ~ %u\n", 0U, UINT_MAX);
    printf("unsigned long     : %lu ~ %lu\n", 0UL, ULONG_MAX);
    printf("unsigned long long: %llu ~ %llu\n", 0ULL, ULLONG_MAX);

    return 0;
}

登場する命令の書式と「何をするか」

#include

  • 書式:#include <ヘッダ名>
  • 何をする:標準ライブラリの宣言やマクロ定義を取り込む
    例:<stdio.h>(printf など)、<limits.h>(INT_MAX など)

puts

  • 書式:puts(文字列);
  • 何をする:文字列を表示して改行する(お知らせ行に便利)

printf

  • 書式:printf(書式文字列, 値, ...);
  • 何をする:値を指定した形式で表示する

書式指定子(範囲表示で重要)

指定子対象
%dint(符号付き)INT_MIN など
%uunsigned intUINT_MAX など
%ldlongLONG_MAX など
%lldlong longLLONG_MAX など
%luunsigned longULONG_MAX など
%lluunsigned long longULLONG_MAX など

図で理解:unsigned は「0から始まる世界」

図:signed と unsigned の範囲イメージ(概念図)

この図が示しているのは、

  • signed は 0 を中心に負側がある
  • unsigned は 0 からスタートして正側に全振り
    という違いです。

よくある指針(使い分けの現場ルール)

表:型選びの実務っぽい指針

やりたいことまず候補理由
普通の整数計算int扱いやすい、速いことが多い
大きな値が必要long / long long範囲が足りないときだけ拡張
0以上しか出ないunsigned範囲を正側に使い切れる
文字を扱うchar / unsigned char文字コードの扱いで使う