閉じる

もう迷わないC言語のint / short / longの違いと選び方【初心者向け】

C言語を学び始めると、intやshort、longの違いで必ず一度はつまずきます。

どれも「整数」を扱う型ですが、サイズや表現できる範囲、環境による違いがあるため、なんとなく選んでいるとバグの原因になります。

本記事では、初心者の方にもわかりやすいように、図解とサンプルコードを交えながら「結局どれをどう使えばよいか」まで丁寧に解説します。

C言語のint / short / longとは

整数型(integer)の基本と役割

整数型は、プログラムで数値を扱うときの「箱」のような役割を持ちます。

箱の大きさとルール(符号の有無など)によって、入れられる数値の範囲が決まります。

int / short / longの違いは、主に「箱の大きさ」と「扱う範囲」の違いだとイメージすると理解しやすくなります。

整数型の役割を整理すると、次のようになります。

  • 短い範囲の整数を、少ないメモリで扱いたいときにshort
  • 標準的な整数型として、もっともよく使われるのがint
  • より大きな値を扱いたいときにlonglong long

ここで重要なのは、これらのサイズ(ビット数)が「どの環境でも同じ」とは限らないという点です。

これが、C言語の型をややこしくしている大きな理由です。

C言語規格で決まっていること・決まっていないこと

C言語の規格(標準仕様)では、各整数型の「最小サイズ」や「相対的な関係」は決めていますが、「何ビット」とまでは決めていません。

規格で決まっていることは、代表的なものだと次のようになります。

  • shortのビット数 ≤ intのビット数 ≤ longのビット数
  • shortは少なくとも16ビット
  • intも少なくとも16ビット
  • longは少なくとも32ビット
  • signed型は負の数も扱える
  • unsigned型は0以上のみ扱える

一方で、決まっていないことも多くあります。

  • intが32ビットとは限らない(16ビットや64ビットも理屈上あり得る)
  • longが32ビットか64ビットかは環境に依存する
  • shortのサイズも環境依存(ただし16ビット以上)

このように、C言語では「ビット数は環境依存だが、型同士の大小関係は決まっている」と覚えておくと良いです。

32bit環境と64bit環境での違い

現在主流のPCは64bit環境がほとんどですが、C言語の型のサイズは「OSやCPU、コンパイラの設計方針」によって変わります。

代表的な2つのモデルを比較してみます。

典型的なサイズは、次のような形です。

モデル例shortintlonglong long
32bit環境(ILP32)16bit32bit32bit64bit
64bit Linux系(LP64)16bit32bit64bit64bit
64bit Windows(LLP64)16bit32bit32bit64bit

特に注意すべきは、64bit環境でもWindowsではlongが32bitのままであることです。

一方、多くのLinux系ではlongが64bitになっています。

この違いが、「同じCコードでも、OSを変えると型のサイズが変わる」というややこしさの原因です。

サイズは、プログラム中でsizeof演算子を使うことで確認できます。

C言語
#include <stdio.h>

int main(void) {
    printf("short:      %zu bytes\n", sizeof(short));
    printf("int:        %zu bytes\n", sizeof(int));
    printf("long:       %zu bytes\n", sizeof(long));
    printf("long long:  %zu bytes\n", sizeof(long long));
    return 0;
}
実行結果
short:      2 bytes
int:        4 bytes
long:       8 bytes
long long:  8 bytes

上記の出力例は、典型的な64bit Linux環境(LP64)の一例です。

実際の環境で実行して、自分の環境のサイズを確認してみると理解が深まります。

int / short / longのサイズと範囲

short型のサイズと表現できる値の範囲

short型は、小さめの整数を扱うための型です。

規格上は「少なくとも16ビット」とされており、多くの環境では16ビット(2バイト)です。

代表的な環境での値の範囲の例を示します(16ビットの場合)。

ビット数最小値最大値
signed short16-3276832767
unsigned short16065535

signedかunsignedかで、扱える範囲が大きく変わることも重要です。

負の数が不要であれば、unsigned shortを使うことで0〜65535までの広い範囲が使えます。

int型のサイズと表現できる値の範囲

int型は、C言語で最も標準的に使われる整数型です。

サイズは環境依存ですが、現在のPC向け環境では32ビット(4バイト)であることが非常に多いです。

32ビット環境・64ビット環境問わず、多くのPCでは次のようになります。

ビット数最小値最大値
signed int32-2,147,483,6482,147,483,647
unsigned int3204,294,967,295

環境によってはintが16ビットである場合もありますが、一般的なPC開発では32ビットと考えて問題ないケースがほとんどです。

ただし、「どんな環境でも32ビット」と思い込むのは危険なので、移植性が重要なコードではsizeof(int)などで確認したり、後述するint32_tなどの固定幅整数型を使うことが推奨されます。

long型・long long型のサイズと範囲

long型はintよりも「長い」(広い範囲を扱える)整数型として定義されています。

ただし、そのビット数は環境により32ビットだったり64ビットだったりします。

一方、long long型は「少なくとも64ビット」であることがC99以降で保証されており、大きな整数を扱いたいときに安心して使える型です。

典型的な範囲を64ビットの場合で示します。

ビット数最小値最大値
signed long (64bit環境LP64)64-9,223,372,036,854,775,8089,223,372,036,854,775,807
unsigned long (64bit)64018,446,744,073,709,551,615
signed long long64-9,223,372,036,854,775,8089,223,372,036,854,775,807
unsigned long long64018,446,744,073,709,551,615

「とても大きな整数が必要なら、とりあえずlong long」と覚えておくと実務では安心です。

longは環境ごとにサイズが違うため、大きな値を狙って使うには少しリスキーです。

signed / unsignedの違いと注意点

整数型には、符号付き(signed)と符号なし(unsigned)があります。

同じビット数でも、表現できる範囲は大きく異なります。

  • signed int(32ビットの例)
    • -2,147,483,648〜2,147,483,647
  • unsigned int(32ビットの例)
    • 0〜4,294,967,295

負の値が絶対に出てこない個数・インデックスなどは、unsignedを使う選択肢がありますが、初心者のうちはむやみにunsignedを多用するとバグの原因になりがちです。

特に注意したいのは、signedとunsignedを混ぜて比較・計算する場合です。

C言語では、演算時に型が暗黙に変換されるため、意図しない結果になることがあります。

int / short / longの使い分けと選び方

初心者が基本的にintを使うべき理由

初心者がまず選ぶべき整数型は、ほぼ常にintです

理由は次の通りです。

文章として整理すると、次のようになります。

  • CPUが最も扱いやすいサイズであることが多く、計算が高速
  • 標準ライブラリやサンプルコードがintを前提としていることが多い
  • 32ビットintなら約±21億まで扱えるため、日常的な数値には十分
  • まずは符号付きint(signed int)だけに絞ることで、型変換の混乱を減らせる

そのため、「特別な理由がなければ、まずint」という指針で問題ありません。

メモリ節約でshortを選ぶべきケース

short型は、メモリを極力節約したいときに検討される型です。

ただし、現代のPC開発ではメモリが潤沢なことが多く、安易にshortに切り替えるメリットは少なくなっています。

shortが有効なケースの例としては、次のような場面です。

  • 組み込み機器など、メモリが数十KB〜数百KBしかない環境でのプログラム
  • センサー値や画像データなど、数万〜数百万要素の配列を扱う場合
  • 値の範囲が-32768〜32767(または0〜65535)に確実に収まることが分かっている場合

サンプルとして、shortとintでメモリ使用量がどう変わるか見てみます。

C言語
#include <stdio.h>

#define N 1000000  /* 要素数100万 */

int main(void) {
    short a[N];    /* short配列 */
    int   b[N];    /* int配列   */

    printf("short配列のサイズ: %zu bytes\n", sizeof(a));
    printf("int配列のサイズ:   %zu bytes\n", sizeof(b));

    return 0;
}
実行結果
short配列のサイズ: 2000000 bytes
int配列のサイズ:   4000000 bytes

このように、要素数が多い配列では、shortを使うことでメモリを半分にできることがあります。

ただし、値が範囲を超えないかどうかを慎重に確認することが前提条件です。

大きな値にはlong longを使う場面

非常に大きな値(32ビットintでは足りない値)を扱うときは、long longを使います

long longは少なくとも64ビットで、約±9.2×10^18まで扱えます。

具体的な例として、階乗やべき乗計算では、intではすぐにオーバーフローしてしまいます。

C言語
#include <stdio.h>

int main(void) {
    int i;
    long long fact = 1;  /* 64ビット整数で階乗を計算 */

    for (i = 1; i <= 20; i++) {
        fact *= i;
        printf("%2d! = %20lld\n", i, fact);
    }

    return 0;
}
実行結果
 1! =                    1
 2! =                    2
 3! =                    6
 4! =                   24
 5! =                  120
 ...
20! = 2432902008176640000

20!は約2.4×10^18で、32ビットintでは絶対に表現できません

このような大きな値を扱う場合は、必ずlong longを使うことが必要です。

ビット幅を固定したいときのstdint.h(int32_tなど)の使い方

環境によってintlongのビット数が変わる問題を避けるために、C99以降ではstdint.hヘッダで「ビット幅が明確な整数型」が用意されています。

代表的な型は次の通りです。

意味
int8_t符号付き8ビット整数
uint8_t符号なし8ビット整数
int16_t符号付き16ビット整数
uint16_t符号なし16ビット整数
int32_t符号付き32ビット整数
uint32_t符号なし32ビット整数
int64_t符号付き64ビット整数
uint64_t符号なし64ビット整数

これらは「そのビット数で定義できる環境でのみ定義される」ため、存在する場合は常に指定通りのビット数になります。

使用例を示します。

C言語
#include <stdio.h>
#include <stdint.h>  /* 固定幅整数型を使うためにインクルード */

int main(void) {
    int32_t  a = 100000;         /* 32ビット符号付き整数 */
    uint16_t b = 65000;          /* 16ビット符号なし整数 */
    int64_t  c = 10000000000LL;  /* 64ビット符号付き整数 */

    printf("a(int32_t)  = %d\n", a);
    printf("b(uint16_t) = %u\n", b);
    printf("c(int64_t)  = %lld\n", c);

    return 0;
}
実行結果
a(int32_t)  = 100000
b(uint16_t) = 65000
c(int64_t)  = 10000000000

ネットワークプロトコルやファイルフォーマットなど、ビット単位で仕様が決まっている場面では、これらの固定幅整数型の使用がほぼ必須です。

printfやscanfでのフォーマット指定子の選び方

整数型をprintfscanfで入出力するときは、型に合わせて正しいフォーマット指定子を使う必要があります。

ここを間違えると、正しい値が表示されなかったり、未定義動作になったりします。

代表的な対応を表にまとめます。

printf(符号付き)printf(符号なし)scanf(符号付き)scanf(符号なし)
short%hd%hu%hd%hu
int%d%u%d%u
long%ld%lu%ld%lu
long long%lld%llu%lld%llu
int32_t (多くの環境)%d%u%d%u
int64_t (多くの環境)%lld%llu%lld%llu

例として、各型を入力・出力してみます。

C言語
#include <stdio.h>

int main(void) {
    short      s;
    int        i;
    long       l;
    long long  ll;

    printf("4つの整数を入力してください(short, int, long, long long):\n");

    /* 型に合わせたフォーマット指定子を使う */
    scanf("%hd %d %ld %lld", &s, &i, &l, &ll);

    printf("short:      %hd\n", s);
    printf("int:        %d\n", i);
    printf("long:       %ld\n", l);
    printf("long long:  %lld\n", ll);

    return 0;
}
実行結果
4つの整数を入力してください(short, int, long, long long):
10 20 30 40
short:      10
int:        20
long:       30
long long:  40

フォーマット指定子と変数型が一致しているかは、コンパイラの警告を有効にするとチェックしやすくなります。

-Wallオプションなどをつけてコンパイルすると、安全性が高まります。

C言語初心者がやりがちなミスと対策

桁あふれ(オーバーフロー)を防ぐint / longの選び方

オーバーフロー(桁あふれ)は、整数型の範囲を超える値を扱ったときに発生する現象です。

C言語では、符号付き整数のオーバーフローは未定義動作とされています。

典型的な失敗例を見てみます。

C言語
#include <stdio.h>
#include <limits.h>  /* INT_MAX, LONG_MAX などの定数 */

int main(void) {
    int a = INT_MAX;
    int b = a + 1;   /* オーバーフローの可能性あり */

    printf("INT_MAX: %d\n", INT_MAX);
    printf("a:       %d\n", a);
    printf("a + 1:   %d (未定義動作の可能性)\n", b);

    return 0;
}
実行結果
INT_MAX: 2147483647
a:       2147483647
a + 1:   -2147483648 (環境によって異なる可能性あり)

この例では、a + 1がオーバーフローして負の値になっていますが、これはたまたまそうなっているだけで、C言語規格上は結果は保証されません

オーバーフローを防ぐためには、次のような対策が有効です。

  • 必要な最大値から逆算して、十分なビット幅の型を選ぶ
  • 不安なら1つか2つ大きめの型(例: intではなくlong long)を使う
  • INT_MAXLLONG_MAXなどの定数で、限界値を意識する
C言語
#include <stdio.h>
#include <limits.h>

int main(void) {
    long long x = LLONG_MAX - 5;  /* long long の最大値付近 */

    printf("LLONG_MAX: %lld\n", LLONG_MAX);
    printf("x:         %lld\n", x);

    if (x + 10 < x) {
        printf("オーバーフローが起きた可能性があります。\n");
    } else {
        printf("まだオーバーフローしていません。\n");
    }

    return 0;
}
実行結果
LLONG_MAX: 9223372036854775807
x:         9223372036854775802
オーバーフローが起きた可能性があります。

このように、計算前に型と最大値を意識することが、オーバーフロー対策の第一歩です。

環境依存(intのビット数が違う)への対処法

同じCコードなのに、環境を変えたらintのサイズが変わってしまう

これが、移植性の問題の典型例です。

対策としては、次のような考え方が有効です。

  • 「intは32ビット」と決めつけたコードを書かない
  • ビット数が重要な場面では、固定幅整数型(int32_tなど)を使う
  • コンパイル時にsizeofを使って、型のサイズを確認するテストコードを仕込む

例として、環境ごとに整数型のサイズを表示するツールを書いておくと便利です。

C言語
#include <stdio.h>

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

    return 0;
}
実行結果
char:      1 bytes
short:     2 bytes
int:       4 bytes
long:      8 bytes
long long: 8 bytes

チーム開発や長期運用されるソフトウェアでは、こうした「環境チェック用ツール」を最初に一度実行しておくと安心です。

キャストや演算での型変換ミスを避けるコツ

C言語では、異なる整数型同士を計算するときに暗黙の型変換(整数の usual arithmetic conversions)が行われます。

これが原因で、初心者が意図しないバグを生むことがよくあります。

代表的な落とし穴をいくつか挙げます。

1. signed と unsigned の混在

C言語
#include <stdio.h>

int main(void) {
    int          a = -1;          /* 符号付き */
    unsigned int b = 1;           /* 符号なし */

    if (a < b) {
        printf("a < b です\n");
    } else {
        printf("a >= b です\n");
    }

    return 0;
}
実行結果
a >= b です

一見a = -1なのでa < bに見えますが、実際にはaがunsignedに変換されて非常に大きな値になり、条件が逆転してしまいます。

対策としては、次のようなことが挙げられます。

  • signedとunsignedを混ぜないように心がける
  • 比較するときは、どちらかに明示的にキャストして意図をはっきりさせる

2. 小さい型から大きい型への変換での誤解

C言語
#include <stdio.h>

int main(void) {
    unsigned char c = 255;  /* 0〜255の範囲 */
    int           i = c;    /* intに拡張される */

    printf("c = %u, i = %d\n", c, i);

    return 0;
}
実行結果
c = 255, i = 255

この例では問題ありませんが、signed charで負の値を扱った場合など、符号拡張のルールを理解していないと意外な結果になることがあります。

3. 明示的なキャストの乱用

C言語
#include <stdio.h>

int main(void) {
    long long big = 10000000000LL;
    int       x = (int)big;  /* 明示的にintへキャスト */

    printf("big = %lld, x = %d\n", big, x);

    return 0;
}
実行結果
big = 10000000000, x = 1410065408

intの範囲を超える値を無理にキャストすると、情報が失われてしまいます

明示的なキャストは、「間違いを隠してしまう」ことがあるため、むやみに使わず、本当に必要な箇所にだけ慎重に使うことが重要です。

まとめ

本記事では、C言語のint / short / long / long longの違いと、それぞれのサイズ・範囲・使い分けの指針を解説しました。

基本的には「特別な理由がなければint」、大きな値が必要ならlong long、ビット幅を固定したい場面ではstdint.hint32_tなどを使う、という方針で考えると整理しやすくなります。

環境依存やオーバーフロー、型変換の落とし穴を意識しつつ、まずはintを中心に安全な整数型の扱い方に慣れていってください。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!