C言語で整数型を選ぶとき、intだけを使っておけば大丈夫と思ってしまいがちですが、実際にはshortやlongを正しく使い分けることがとても重要です。
この記事では、C言語初心者の方に向けて、整数型の基礎から、int・short・longの違い、実際の選び方や注意点まで、サンプルコードを交えながら詳しく解説していきます。
C言語の整数型(int / short / long)とは?
整数型とは何か
C言語における整数型は、小数点を含まない数値を扱うための型です。
例えば、次のような値を扱います。
- 0、1、-1、100、-256 などの整数
- 個数、回数、インデックス(配列の添字)など
整数型は、主に次のような目的で使われます。
- ループ回数や配列の添字を表す
- フラグや状態を数値で表す
- ファイルサイズやバイト数などを表す
整数型にはint、short、longなど複数の種類があり、それぞれ表現できる範囲(最小値~最大値)や占めるメモリのサイズが異なります。
int・short・longの基本的な役割
C言語では、整数型を大きく分けると次のようになります。
- short
比較的小さな範囲の整数を扱うための型です。
古くはメモリ節約のためによく使われていました。 - int
基本となる整数型です。多くの場合、整数を扱うときはまずintを使います。 - long
intよりも広い範囲の整数を扱うための型です。
ファイルサイズなど、大きな値を扱うときに使われます。
この3つは、「大きさ」(表現できる範囲)が違う兄弟のような関係です。
ただし、「shortが必ず16ビット」「intが必ず32ビット」とは限らないところが、C言語の少し難しい点です。
32bit/64bit環境と整数型の「大きさ」の関係
C言語では、整数型のビット数は規格で完全には固定されていません。
ただし、次のような関係(サイズの順序)だけは必ず守られます。
sizeof(short) <= sizeof(int) <= sizeof(long)
つまり、shortはint以下、intはlong以下のサイズになります。
一般的な環境ごとの例を表にまとめます。
| 環境の例 | short | int | long |
|---|---|---|---|
| 32bit OS(古めの環境など) | 16ビット(2バイト) | 32ビット(4バイト) | 32ビット(4バイト) |
| 64bit OS(多くのWindowsなど) | 16ビット(2バイト) | 32ビット(4バイト) | 32ビット(4バイト) |
| 64bit Linux系(多くのUnix系OS) | 16ビット(2バイト) | 32ビット(4バイト) | 64ビット(8バイト) |
このように、32bit環境か64bit環境かで、特にlongのビット数が変わることがあります。
そのため、「longは必ず32ビット」などと決め打ちしないことが大切です。
実際にどのくらいのサイズかは、sizeof演算子で確認できます。
int・short・longのサイズと表現できる範囲
intのビット数と表現範囲
多くの環境では、intは32ビット(4バイト)で実装されています。
ただし、古い組み込み環境では16ビットのこともあり得ます。
32ビットの符号付きintの場合、表現できる範囲は次の通りです。
| 型 | ビット数(例) | 表現範囲(10進数) |
|---|---|---|
| signed int | 32ビット | -2,147,483,648 ~ 2,147,483,647 |
| unsigned int | 32ビット | 0 ~ 4,294,967,295 |
ここでのポイントは、signedかunsignedかで負の値を扱えるかどうかが変わるという点です。
shortのビット数と表現範囲
shortは、intよりも小さいサイズの整数型です。
多くの環境で、shortは16ビット(2バイト)です。
| 型 | ビット数(例) | 表現範囲(10進数) |
|---|---|---|
| signed short | 16ビット | -32,768 ~ 32,767 |
| unsigned short | 16ビット | 0 ~ 65,535 |
小さな整数値で十分な場合に使われますが、現代のPCではメモリの制約がそれほど厳しくないため、初心者は無理にshortを多用する必要はありません。
longのビット数と表現範囲
longは、intよりも広い範囲を扱うための整数型です。
- 32bit環境や多くのWindows 64bit環境では、32ビット
- 多くのLinux 64bit環境では、64ビット
となっていることが多いです。
32ビットlong(多くのWindowsなど)の例:
| 型 | ビット数 | 表現範囲 |
|---|---|---|
| signed long | 32ビット | -2,147,483,648 ~ 2,147,483,647 |
| unsigned long | 32ビット | 0 ~ 4,294,967,295 |
64ビットlong(多くのLinux 64bitなど)の例:
| 型 | ビット数 | 表現範囲 |
|---|---|---|
| signed long | 64ビット | 約 -9.22e18 ~ 9.22e18 |
| unsigned long | 64ビット | 0 ~ 約 1.84e19 |
ファイルサイズ、メモリアドレス、時間(ミリ秒の累積など)を扱うときにlongが使われることが多いです。
signed / unsignedの違いと使い分け
整数型にはsigned(符号付き)とunsigned(符号なし)があります。
- signed
正と負の両方を表現します。
例:cst-code>int、short、longなど(指定しなければ通常signed) - unsigned
0以上の値のみを表現しますが、その分最大値が2倍近く大きくなります。
例:unsigned int、unsigned short、unsigned long
使い分けの目安は次の通りです。
- 負の値を使う可能性があるなら、必ずsigned型を使う
- 値が0以上と決まっていて、かつ最大値を少しでも広げたいときにunsignedを使う
- 初心者のうちはsignedとunsignedを混ぜた計算をできるだけ避ける
(思わぬ変換やバグの原因になりやすいため)
なぜintだけでは足りないのか
shortを使う場面
現代のPCでは、メモリが比較的豊富なので、単にメモリ節約のためにshortを選ぶ必要はあまりありません。
しかし、次のような場面ではshortが役に立ちます。
- 組み込み機器やマイコンなど、メモリが非常に小さい環境
- 大量のデータを配列で保持するときで、値の範囲が小さいと分かっている場合
(例:0~100程度の値を100万個持つなど) - ファイルフォーマットや通信プロトコルなどで、データサイズが仕様で16ビットと決められている場合
初心者のうちは、「外部仕様で16ビットと決まっている場合」や「教科書の例で使われている場合」にshortを使うくらいでも十分です。
longを使う場面
intでは足りないほど大きな数値を扱うときにlongが必要になります。
典型的な例は次の通りです。
- ファイルのサイズ(数GB以上を扱う可能性がある場合)
- 経過時間をミリ秒でカウントして、長時間動作するプログラム
- 大きな配列のサイズ、メモリサイズを扱う場合
例えば、32ビットintの最大値は約2.1×10^9なので、2,147,483,647ミリ秒は約24.8日です。
「ミリ秒単位で100日動かしたい」などの要件があると、intでは足りず、longなどより大きな型が必要になります。
オーバーフローの危険性と確認ポイント
オーバーフローとは、整数型で表現できる最大値を超えてしまうことです。
例として、32ビットの符号付きintで2,147,483,647に1を足した場合、正しい結果は2,147,483,648ですが、intでは表現できません。
このときの動作はC言語では未定義動作であり、予測できない結果になります。
オーバーフローを避けるために、次のポイントを意識することが大切です。
- 変数の最大値/最小値がどのくらいになるかを事前に見積もる
- 10倍、100倍などを繰り返す処理では急激に値が大きくなることを意識する
- 必要であればlongやlong longを使う
INT_MAX、LONG_MAXなど、limits.hで定義されている定数を使って限界を確認する
次のサンプルで、intの最大値とオーバーフローの挙動を確認してみます。
#include <stdio.h>
#include <limits.h> /* INT_MAX, INT_MIN などの定数を使うため */
int main(void) {
int max = INT_MAX; /* int 型で表せる最大値 */
int min = INT_MIN; /* int 型で表せる最小値 */
printf("INT_MAX = %d\n", max);
printf("INT_MIN = %d\n", min);
/* オーバーフローをわざと発生させてみる */
int overflow = max + 1;
printf("INT_MAX + 1 (オーバーフロー) = %d\n", overflow);
return 0;
}
INT_MAX = 2147483647
INT_MIN = -2147483648
INT_MAX + 1 (オーバーフロー) = -2147483648
この結果は一例であり、オーバーフロー時の動作は処理系依存で保証されません。
「こう表示されたからといって、常にこの動作に頼ってはいけない」という点が重要です。
printfやscanfでのフォーマット指定子に注意
整数をprintfやscanfで扱うときは、型に合ったフォーマット指定子を使う必要があります。
これを間違えると、値が正しく表示されなかったり、scanfでバグの原因になります。
代表的な指定子を表にまとめます。
| 型 | printfの指定子 | scanfの指定子 |
|---|---|---|
| int | %d | %d |
| unsigned int | %u | %u |
| short | %hd | %hd |
| unsigned short | %hu | %hu |
| long | %ld | %ld |
| unsigned long | %lu | %lu |
型とフォーマット指定子が必ず対応していることを確認する習慣をつけると、バグを減らせます。
次のサンプルでは、short、int、longをそれぞれ入力・出力してみます。
#include <stdio.h>
int main(void) {
short s;
int i;
long l;
printf("short, int, long の値を順に入力してください: ");
/* short 用に %hd、int 用に %d、long 用に %ld を使う */
if (scanf("%hd %d %ld", &s, &i, &l) != 3) {
printf("入力エラーです。\n");
return 1;
}
printf("short の値: %hd\n", s);
printf("int の値: %d\n", i);
printf("long の値: %ld\n", l);
return 0;
}
short, int, long の値を順に入力してください: 10 20 30
short の値: 10
int の値: 20
long の値: 30
フォーマット指定子を間違えると、&s にintとして読み込もうとするなどして、メモリの破壊や予期しない値になりますので、必ず型を確認してください。
初心者向け 整数型の選び方と実践テクニック
基本はintを使うのが安全な理由
初心者のうちは、整数はとにかくintで宣言するくらいの感覚でかまいません。
その理由は次の通りです。
- intは「その環境で扱いやすいサイズ」として定義されている
コンパイラやCPUが最も効率よく扱える整数型であることが多いです。 - 教科書やサンプルコードの多くがintを前提にしている
学習時の混乱を減らせます。 - shortやlongを積極的に使うと、ビット数を意識する必要が増え、かえって難しくなる
まずはintでロジックを理解し、その後必要に応じて型を変える方が学習としても効率的です。
「特別な理由がなければint」と覚えておくとよいです。
short / longを選ぶときの判断基準
短いルールでまとめると、次のようになります。
- shortを選ぶとき
- 値の範囲が小さいと明確に分かっている(例えば-100~100など)
- 大量のデータを扱うため、メモリ節約が重要
- プロトコルやファイル形式などの仕様で「16ビット整数」と決められている
- longを選ぶとき
- intの最大値(約2.1×10^9)では足りない可能性がある
- OSやライブラリのAPIでlong型を要求されている
- ファイルサイズ、経過時間、メモリサイズなど、大きくなりやすい値を扱う
まずintで設計し、必要な箇所だけshortやlongに変えるという順番で考えるのがおすすめです。
エラーやバグを防ぐためのチェックリスト
整数型を選ぶとき、次のポイントを確認すると、エラーやバグをかなり防ぐことができます。
- 値の最小値と最大値をざっくりでもいいので見積もったか
- 負の値をとる可能性があるかどうかを確認したか
- 他の変数との型の一致を確認したか(intとunsigned intを混ぜていないかなど)
- printf/scanfのフォーマット指定子が正しいか
- ループカウンタや配列のインデックスでオーバーフローしないか
sizeofやINT_MAXなどを使って動作環境の実際のサイズを確認したか
このようなチェックを一通り行うだけで、整数型に起因するバグを大きく減らせます。
サンプルコードで見るint・short・longの書き方
最後に、int、short、longの宣言と入出力、sizeofによるサイズ確認をまとめて行うサンプルコードを示します。
#include <stdio.h>
#include <limits.h> /* 各種整数型の最大値・最小値を使うため */
int main(void) {
/* それぞれの型の変数を宣言 */
short s = 100; /* 比較的小さい範囲の整数 */
int i = 100000; /* 基本となる整数型 */
long l = 1000000000L; /* より大きな値を扱える整数型。末尾の L は long のリテラル */
printf("=== 各整数型のサイズ ===\n");
printf("sizeof(short) = %zu バイト\n", sizeof(short));
printf("sizeof(int) = %zu バイト\n", sizeof(int));
printf("sizeof(long) = %zu バイト\n", sizeof(long));
printf("\n=== 各整数型の値 ===\n");
printf("short s = %hd\n", s); /* short 用に %hd */
printf("int i = %d\n", i); /* int 用に %d */
printf("long l = %ld\n", l); /* long 用に %ld */
printf("\n=== 各整数型の表現可能範囲(一例) ===\n");
printf("SHRT_MIN = %d, SHRT_MAX = %d\n", SHRT_MIN, SHRT_MAX);
printf("INT_MIN = %d, INT_MAX = %d\n", INT_MIN, INT_MAX);
printf("LONG_MIN = %ld, LONG_MAX = %ld\n", LONG_MIN, LONG_MAX);
/* ユーザーから値を入力して確認する例 */
printf("\nshort, int, long の値を順に入力してください: ");
if (scanf("%hd %d %ld", &s, &i, &l) != 3) {
printf("入力エラーです。\n");
return 1;
}
printf("入力された short: %hd\n", s);
printf("入力された int : %d\n", i);
printf("入力された long : %ld\n", l);
return 0;
}
=== 各整数型のサイズ ===
sizeof(short) = 2 バイト
sizeof(int) = 4 バイト
sizeof(long) = 8 バイト
=== 各整数型の値 ===
short s = 100
int i = 100000
long l = 1000000000
=== 各整数型の表現可能範囲(一例) ===
SHRT_MIN = -32768, SHRT_MAX = 32767
INT_MIN = -2147483648, INT_MAX = 2147483647
LONG_MIN = -9223372036854775808, LONG_MAX = 9223372036854775807
short, int, long の値を順に入力してください: 10 20 30
入力された short: 10
入力された int : 20
入力された long : 30
この出力例では、longが8バイト(64ビット)になっているLinux系64bit環境の一例を示しています。
お使いの環境ではサイズや範囲が異なる可能性があるため、ぜひ自分でこのプログラムを実行して確認してみてください。
まとめ
この記事では、C言語の整数型(int / short / long)の使い分けについて、初心者向けに詳しく解説しました。
重要なポイントを整理すると、次のようになります。
- 整数型にはshort、int、longがあり、サイズと表現範囲が違う
- ビット数は環境により異なりますが、
sizeof(short) <= sizeof(int) <= sizeof(long)の関係は必ず守られる - 基本はintを使い、特別な理由があるときだけshortやlongを使う
- signedとunsignedを正しく理解し、混在させるときは要注意
- オーバーフローは未定義動作であり、最大値・最小値を意識して型を選ぶ必要がある
- printf/scanfのフォーマット指定子を型に合わせることが、バグ防止の基本になる
sizeofやlimits.hを使って、実際の環境でのサイズと範囲を自分で確認することが大切
C言語では、「どの整数型を使うか」という設計の一つ一つが、プログラムの正しさや安全性に直結します。
まずはintでシンプルに書き、必要に応じてshortやlong、signed/unsignedの使い分けを学んでいくことで、より堅牢なプログラムを書けるようになります。
