C言語のきほん|整数型の符号とサイズを理解する

整数型の違いがわかると、C言語のデータの見え方がぐっと深くなる。

C言語で整数を扱うとき、まず思い浮かぶのは int かもしれません。
実際、整数を扱う場面では int がよく使われますし、入門の段階でも最初に登場することが多い型です。

でも、C言語の整数型は int だけではありません。
char、short、long、long long、そしてそれぞれに対応する unsigned 系の型もあります。さらに、見た目は似ていても、負の数を扱える型と、0以上の値だけを扱う型では意味が大きく異なります。

ここで大切なのは、整数型には「符号」と「サイズ」という2つの視点があることです。
符号は、負の数を扱えるかどうかを表します。
サイズは、メモリ上で何バイト分の領域を使うかを表します。

この2つを意識して見るようになると、なぜ同じ整数でもいろいろな型があるのか、なぜ扱える値の範囲が違うのかが、とてもわかりやすくなります。
また、型の選び方を誤ると、思わぬオーバーフローや比較の失敗につながることもあるため、整数型の性質をきちんと理解しておくことはとても大切です。

この内容は、単に型の名前を覚えるためのものではありません。
C言語らしい「メモリを意識したデータの扱い方」を学ぶための大事な入り口でもあります。
ここでは、整数型の符号指定とサイズ指定の考え方を整理しながら、それぞれの型の特徴を丁寧に見ていきましょう。

整数型の符号指定とは何か

C言語の整数型は、まず符号の有無によって大きく2つに分けられます。

分類意味
signed負の数、0、正の数を扱える
unsigned0 と正の数を扱える

signed は「符号付き整数」、unsigned は「符号なし整数」です。

たとえば、signed int は負の値も扱えますが、unsigned int は負の値を扱えません。
その代わり、unsigned int は負の値を使わないぶん、より大きな正の数まで表せます。

この違いはとても重要です。
たとえば、温度のようにマイナスの値が出てくるデータなら signed 系が向いています。
一方で、個数やサイズのように絶対に負にならない値なら unsigned 系が候補になります。

符号付き整数の特徴

符号付き整数は、負の数を含めて扱える型です。
普段よく使う int は、通常は signed int の省略形として使われます。

たとえば、次のような値を扱えます。

  • -10
  • 0
  • 25

このように、正負をまたぐ値を自然に扱えるのが signed の特徴です。

符号なし整数の特徴

符号なし整数は、0と正の値だけを扱う型です。
負の値は扱えません。

たとえば unsigned int なら、0、1、100、1000 のような値は扱えますが、-1 のような負の値は表せません。

その代わり、同じサイズの signed 型よりも大きな正の値まで保存できます。
これは、負の数を表すための分をすべて正の値の表現に使えるからです。

char の符号は特別であることに注意する

ここで少し注意したいのが char です。
char は1バイトの整数型ですが、char とだけ書いたとき、それが signed 扱いになるか unsigned 扱いになるかは処理系に依存します。

つまり、環境によっては次のどちらかになります。

  • signed char と同じように扱われる
  • unsigned char と同じように扱われる

このため、符号をはっきりさせたいときは、char ではなく signed char や unsigned char と明示して書くのが安心です。

整数型のサイズ指定とは何か

整数型は、符号だけでなく、サイズによっても分類されます。
サイズとは、メモリ上で何バイト使うかという意味です。

C言語では、整数型のサイズを表す指定子として次のようなものがあります。

指定子意味
short小さめの整数型
int標準的な整数型
longより大きな整数型
long longさらに大きな整数型

これらは「どれが必ず何バイト」と完全に固定されているわけではありません。
処理系によって違いがあります。
ただし、多くの環境では次のようなサイズがよく見られます。

よくあるサイズの例
short2バイト
int4バイト
long4バイト または 8バイト
long long8バイト以上

ここで大切なのは、サイズが大きいほど、一般に表せる値の範囲も広くなるということです。

short の考え方

short は、int より小さい範囲の整数を扱うための型です。
一般的には 2バイトであることが多く、比較的小さな数値を扱うときに使われます。

たとえば、組込み開発のようにメモリを少しでも節約したい場面では、short を使う意味が出てきます。

int の考え方

int は、C言語で最も標準的な整数型です。
多くの環境で効率よく扱えるように設計されているため、特別な理由がなければ整数には int を使うことが多いです。

「整数を扱うならまず int を考える」というのは、とても自然な考え方です。

long の考え方

long は、int より大きな範囲を扱うことを意図した型です。
ただし、実際のサイズは環境によって違います。

たとえば、Windows系の環境では long が 4バイトのことが多いですが、64ビットUnix系では 8バイトになることがあります。
この違いは、移植性を考えるときに特に意識したいポイントです。

long long の考え方

long long は、さらに大きな整数を扱うための型です。
少なくとも 8バイト以上であることが保証されています。

かなり大きな数を扱う必要があるときには、long long が活躍します。
たとえば、大きなカウント値やファイルサイズの計算などで使われることがあります。

符号とサイズを組み合わせて型が決まる

C言語の整数型は、符号指定とサイズ指定を組み合わせて作られます。
これを見ると、型の名前が整理しやすくなります。

整数型の組み合わせ

分類
符号付き + 小さいサイズshort
符号なし + 小さいサイズunsigned short
符号付き + 標準サイズint
符号なし + 標準サイズunsigned
符号付き + 大きめサイズlong
符号なし + 大きめサイズunsigned long
符号付き + さらに大きいサイズlong long
符号なし + さらに大きいサイズunsigned long long

こうして並べてみると、C言語の整数型はばらばらに見えて、実はかなり規則的に作られていることがわかります。

整数型の種類を整理して確認する

ここでは、よく使う整数型をひとつずつ整理して見ていきましょう。

int 型

int は、C言語で最も基本的な整数型です。
整数を自然に扱うとき、まず候補になるのがこの型です。

多くの環境で効率よく処理されるため、特別な理由がなければ int を選ぶことが一般的です。
負の値も扱えるので、日常的な計算にはとても使いやすい型です。

char 型

char は文字を扱う型としてよく紹介されますが、実際には 1バイトの整数型でもあります。
文字は内部では数値で表されるため、char は「文字用でありながら整数型でもある」という少し特別な存在です。

ただし、char 単体の符号は処理系依存なので、負の値を扱う可能性があるかどうかを明確にしたいときは、signed char や unsigned char を使うのが安心です。

bool 型

bool は論理値を表す型です。
真と偽をわかりやすく扱うために使います。

C言語では本来 _Bool という型があり、stdbool.h をインクルードすると bool、true、false が使えるようになります。

昔は int を使って 0 を偽、0以外を真として表していましたが、bool を使うことで、コードの意味がずっと読み取りやすくなります。

short 型と unsigned short 型

short は int より小さい範囲の整数を扱う型です。
一般的には 2バイトで、小さめの値を扱う場面に向いています。

unsigned short はその符号なし版で、負の値を扱えない代わりに、より大きな正の値まで保存できます。

long 型と unsigned long 型

long は、int より大きな値を扱うための型です。
ただし、環境によって int と同じ大きさになることもあります。

unsigned long はその符号なし版です。
値の範囲が大きく必要で、しかも負の値が不要なときに使います。

long long 型と unsigned long long 型

long long は、かなり大きな整数を扱える型です。
最低でも 8バイトが保証されているため、大きな整数を安全に扱いたいときに頼りになります。

unsigned long long はその符号なし版で、非常に大きな正の整数を扱えます。

型のサイズと値の範囲は環境によって異なる

ここはとても大切なポイントです。
C言語では、整数型のサイズは処理系に依存します。

たとえば、Windows系では long が 4バイトのことが多いですが、別の環境では 8バイトになることがあります。
そのため、「long は必ずこの大きさ」と決めつけるのは危険です。

サイズや範囲を確認したいときは、実際に自分の環境で調べるのがいちばん確実です。
そのために使うのが sizeof 演算子と limits.h です。

  • sizeof は型のサイズを調べる
  • limits.h は整数型の最小値と最大値を確認する

この2つを使うことで、自分の環境での整数型の特徴を正確に知ることができます。

printf の変換指定にも注意する

整数型を表示するときは、型に合った変換指定を使う必要があります。
ここを間違えると、正しく表示されなかったり、意図しない結果になることがあります。

10進数で表示するときの主な変換指定

型名変換指定
short%hd
unsigned short%hu
int%d
unsigned%u
long%ld
unsigned long%lu
long long%lld
unsigned long long%llu

char と bool は少し特別です。
printf には char 専用や bool 専用の10進変換指定はありません。
これらは可変長引数に渡されるときに int に整数拡張されるため、%d を使って表示します。

char と bool に %d を使う理由

printf で使う指定理由
char%dint に拡張されるため
bool%dint に拡張されるため

この点は最初少し不思議に感じるかもしれませんが、「printf に渡すときには小さな整数型は int として扱われる」と考えると理解しやすいです。

サンプルプログラム

「いくつかの整数型に値を入れ、サイズと値を表示しながら違いをつかむ」サンプルプログラムです。

ファイル名:16_2_1.c

#include <stdio.h>
#include <stdbool.h>

int main(void)
{
    /* いろいろな整数型の変数を用意する */
    char letter_code = 65;
    short small_number = 1200;
    int score = -500;
    unsigned item_count = 3000;
    long population = 1234567L;
    long long big_value = 9000000000LL;
    bool is_ok = true;

    /* サイズと値を表示する */
    printf("各整数型の特徴を確認してみましょう。\n\n");

    printf("char のサイズは %zuバイト、値は %d です。\n",
           sizeof(letter_code), letter_code);

    printf("short のサイズは %zuバイト、値は %hd です。\n",
           sizeof(small_number), small_number);

    printf("int のサイズは %zuバイト、値は %d です。\n",
           sizeof(score), score);

    printf("unsigned のサイズは %zuバイト、値は %u です。\n",
           sizeof(item_count), item_count);

    printf("long のサイズは %zuバイト、値は %ld です。\n",
           sizeof(population), population);

    printf("long long のサイズは %zuバイト、値は %lld です。\n",
           sizeof(big_value), big_value);

    printf("bool のサイズは %zuバイト、値は %d です。\n",
           sizeof(is_ok), is_ok);

    return 0;
}

このサンプルプログラムでわかること

このプログラムでは、各整数型に実際に値を入れて表示しています。
そのため、単に型名を眺めるだけではなく、「この型はこういう値を持てるのか」「表示するときはこの指定を使うのか」という感覚がつかみやすくなります。

特に注目したいのは次の点です。

注目ポイント内容
sizeof の結果型ごとに使うバイト数が違う
変換指定型に応じて printf の指定が変わる
bool の表示true は 1 として表示される
char の表示数値として見ると 65 のように表示できる

char は文字のためだけの型ではなく、1バイトの整数型でもあることが、この例でも感じ取りやすいです。

この図は、符号付き整数と符号なし整数の違いを、まず感覚的につかむためのものです。
符号付き整数は負の値を含めて扱えるのに対して、符号なし整数は 0 と正の値だけを扱います。

この違いを最初にしっかり押さえておくと、unsigned を使ったときに「なぜ負の値が入らないのか」「なぜ最大値が大きくなるのか」が理解しやすくなります。

この図は、整数型のサイズ指定の違いをひと目で比較するための図です。
short、int、long、long long は名前が似ていて少し混乱しやすいですが、並べて見ることで「だいたいどちらが大きいか」がつかみやすくなります。

ただし、long は環境によってサイズが違うことがあるため、そこを図の中でもきちんと強調しておくと誤解が減ります。
この点は、C言語の整数型を学ぶうえでとても大切です。

固定サイズ整数型という考え方にも少し触れておきたい

整数型のサイズが処理系依存だと聞くと、「じゃあ常に同じサイズで使いたいときはどうするのだろう」と思うかもしれません。
そのようなときに使うのが stdint.h に定義されている固定サイズ整数型です。

たとえば、次のような型があります。

型名意味
int8_t8ビットの符号付き整数
uint16_t16ビットの符号なし整数
int32_t32ビットの符号付き整数
uint64_t64ビットの符号なし整数

これらを使うと、サイズを明確に指定しやすくなります。
とくにファイル形式、通信データ、組込み開発などではとても重要です。

今回は中心テーマではありませんが、「C言語には処理系依存の整数型だけでなく、サイズを明確にした型もある」ということを知っておくと、この先の学習がよりスムーズになります。

整数型を選ぶときの考え方

整数型を選ぶときは、ただ何となく書くのではなく、次のような視点で考えると選びやすくなります。

視点考えること
負の値が必要か必要なら signed 系を考える
どのくらい大きな値を扱うか範囲に応じて short、int、long、long long を選ぶ
環境差を避けたいか必要なら stdint.h の固定サイズ型も検討する
可読性はどうか通常の整数ならまず int が自然

実際には、普段の整数計算なら int がとても使いやすいです。
ただし、値の範囲が大きい、符号が不要、サイズを明確にしたい、といった条件がある場合には、ほかの型を選ぶ意味が出てきます。

このように「なぜその型を選ぶのか」を意識できるようになると、コードがぐっと読みやすく、意図も伝わりやすくなります。

必要であればこの続きとして、同じ文体のまま
2の補数の解説整数型のオーバーフローの説明練習問題つきの記事化 に続けて整えることもできます。