C言語を用いた開発において、変数の「型」と「メモリサイズ」を正確に把握することは、効率的でバグのないプログラムを記述するための第一歩です。

C言語はハードウェアに近いレイヤーを扱う言語であるため、他の高水準言語と比較して、メモリの消費量やデータの表現範囲を開発者自身が厳密に管理する必要があります。

特に、実行環境(CPUアーキテクチャやコンパイラ)によって型のサイズが変動する場合があるという点は、移植性の高いコードを書く上で避けては通れない知識です。

本記事では、C言語における主要なデータ型のサイズ一覧から、実行環境でサイズを確認するためのsizeof演算子の使い方、各型が保持できる値の範囲、そして実務で役立つ型定義のテクニックまで、エンジニアが知っておくべき情報を網羅的に解説します。

C言語の基本データ型と一般的なサイズ一覧

C言語には、整数を扱う型、浮動小数点数を扱う型、文字を扱う型など、複数の基本データ型が存在します。

まずは、一般的な64bit環境(LP64モデル)における主要な型のサイズを確認しましょう。

型の種類型名サイズ(バイト)役割
文字型char11文字、または最小単位の整数を扱う
整数型short2短い整数を扱う
整数型int4基本的な整数を扱う(環境依存が強い)
整数型long4 または 8長い整数を扱う(OSにより異なる)
整数型long long8非常に長い整数を扱う
単精度浮動小数点型float4小数点を含む数値を扱う
倍精度浮動小数点型double8高精度な小数点数値を扱う
拡張精度浮動小数点型long double12 または 16極めて高い精度の小数点数値を扱う

これらのサイズはあくまで「一般的」なものであり、C言語の規格(ISO/IEC 9899)では、各型が最低限満たすべきサイズのみが規定されています。

そのため、「int型は必ず4バイトである」と決めつけるのは危険です。

環境による型のサイズの違い(データモデル)

C言語の型のサイズは、CPUのアーキテクチャ(32bit/64bit)やOSの仕様によって決まる「データモデル」に依存します。

代表的なモデルには以下のものがあります。

  • LP64:LinuxやmacOSなどの64bit Unix系OSで採用。long型とポインタ型が64bit(8バイト)。
  • LLP64:64bit版Windowsで採用。long型は32bit(4バイト)のまま、long long型とポインタ型が64bit。
  • ILP32:一般的な32bit環境。int、long、ポインタ型のすべてが32bit(4バイト)。

このように、同じ64bit環境であってもWindowsとLinuxではlong型のサイズが異なるため、マルチプラットフォーム向けのプログラムを開発する際には特に注意が必要です。

sizeof演算子によるサイズ確認方法

プログラムの実行環境において、ある型が実際に何バイトを占有しているかを確認するには、sizeof演算子を使用します。

sizeof演算子の基本的な使い方

sizeofは関数ではなく「演算子」です。

引数として型名や変数名を指定することで、そのデータのサイズをバイト単位で返します。

戻り値の型は、符号なし整数型であるsize_tです。

C言語
#include <stdio.h>

int main(void) {
    int n = 10;
    double d = 1.23;

    // 型名を指定する場合
    printf("int型のサイズ: %zu バイト\n", sizeof(int));
    
    // 変数名を指定する場合
    printf("変数nのサイズ: %zu バイト\n", sizeof(n));
    printf("変数dのサイズ: %zu バイト\n", sizeof(d));

    return 0;
}

上記のコードで、%zusize_t型を安全に出力するための書式指定子です。

古いコンパイラでは%luを使用することもありますが、最新の標準に従う場合は%zuを使用することが推奨されます。

配列の要素数を求める応用テクニック

sizeofは、配列全体のサイズを取得する際にも非常に便利です。

配列全体のサイズを、配列の先頭要素のサイズで割ることで、要素数を動的に算出できます。

C言語
int array[] = {1, 2, 3, 4, 5};
size_t length = sizeof(array) / sizeof(array[0]);
printf("配列の要素数: %zu\n", length); // 5が出力される

ただし、関数に配列を渡した場合、関数内では配列はポインタとして扱われるため、関数内でこの手法を使っても正しい要素数は取得できません。

この点はC言語初心者が非常につまずきやすいポイントです。

型が保持できる値の範囲

各データ型には、そのサイズ(ビット数)に応じて表現できる数値の最小値と最大値が決まっています。

これらはlimits.h(整数型)やfloat.h(浮動小数点型)というヘッダーファイルで定義されています。

整数型の範囲(signed と unsigned)

整数型には、正負の値を扱うsigned(デフォルト)と、正の値のみを扱うunsignedがあります。

ビット数最小値最大値
signed char8-128127
unsigned char80255
short16-32,76832,767
unsigned short16065,535
int (32bit)32-2,147,483,6482,147,483,647
unsigned int (32bit)3204,294,967,295

コンピュータ内部では整数は「2進数」で表現されており、負の数は通常「2の補数」形式で保持されます。

例えば8ビット(1バイト)の場合、2の8乗である256通りの値を表現できます。

これを正負に分けると-128〜127になり、符号なしにすると0〜255になります。

浮動小数点型の精度と範囲

浮動小数点型は、IEEE 754という標準規格に基づいて実装されていることが一般的です。

  • float型:有効桁数は約7桁。物理演算や画像処理など、メモリ消費を抑えたい場合に使用されます。
  • double型:有効桁数は約15桁。科学技術計算や金融計算など、精度が求められる標準的な場面で使用されます。

浮動小数点数は、巨大な数値や極小の数値を扱えますが、「計算誤差」が発生する可能性があることに注意が必要です。

例えば、0.1を何度も加算すると、微小な誤差が蓄積されます。

実務で重要な「固定幅整数型」の活用

前述の通り、intlongのサイズは環境によって変動します。

通信プロトコルの実装やバイナリデータの解析など、1ビットの狂いも許されない場面では、C99規格で導入されたstdint.hを使用するのが現代的な作法です。

stdint.h で定義されている型

このヘッダーを使用すると、サイズが保証された型を利用できます。

  • int8_t / uint8_t:8ビット(1バイト)
  • int16_t / uint16_t:16ビット(2バイト)
  • int32_t / uint32_t:32ビット(4バイト)
  • int64_t / uint64_t:64ビット(8バイト)
C言語
#include <stdint.h>

int32_t price = 1000;  // どんな環境でも必ず32ビット
uint8_t flag = 1;      // どんな環境でも必ず8ビット

移植性の高いプログラムを書くためには、基本のint型よりもこれらの固定幅整数型を積極的に利用することが推奨されます。

これにより、32bitマイコンから64bitサーバーサイドプログラムまで、同じ動作を期待できるコードになります。

構造体のサイズとアライメントの注意点

型のサイズを理解する上で、もう一つ重要な概念が「構造体のアライメント(整列)」です。

構造体の中に異なる型の変数を並べたとき、全体のサイズは単なる各型の合計値になるとは限りません。

パディングによるサイズ増加

CPUが効率よくメモリにアクセスするために、データは特定の境界(4バイト境界や8バイト境界など)に合わせて配置されます。

このとき、データの隙間に挿入される無意味なデータを「パディング」と呼びます。

C言語
struct Sample {
    char a;     // 1バイト
    // ここに3バイトのパディングが入る
    int b;      // 4バイト
    char c;     // 1バイト
    // ここに3バイトのパディングが入る
};

上記の構造体Sampleの場合、個別の合計は1 + 4 + 1 = 6バイトですが、sizeof(struct Sample)を実行すると12バイト(環境による)になることがあります。

メモリ使用量を節約したい場合は、「サイズの大きい型から順に並べる」ことで、パディングを最小限に抑えることが可能です。

まとめ

C言語における型のサイズは、単なる基礎知識に留まらず、システムのパフォーマンスや安定性に直結する重要な要素です。

  • 基本型のサイズは環境(データモデル)に依存することを理解する。
  • 実行環境での正確なサイズ確認にはsizeof演算子を活用する。
  • 移植性を重視する場合はstdint.hの固定幅整数型を使用する。
  • 構造体ではアライメントによって予期せぬパディングが発生することを意識する。

これらのルールを徹底することで、メモリ効率が良く、かつ他の環境へ移植した際にもバグの出にくい堅牢なプログラムを構築できるようになります。

特に、昨今のIoT機器開発や組み込みシステム、あるいは大規模なバックエンドシステムにおいて、「1バイトを大切にする」設計思想は、エンジニアとしての資質を問われる重要なポイントと言えるでしょう。

まずは、自分の開発環境でsizeofを使って、各型がどのようなサイズになっているかを実際に確認することから始めてみてください。