閉じる

C言語のデータ型まとめ 種類の違いと選び方、値の範囲を解説

C言語のプログラムでは、データを「どう表現するか」を決めるデータ型がとても重要です。

本記事では基本の型の違い、値の範囲、選び方の指針を、初心者でも迷わないよう順序立てて解説します。

実装依存の落とし穴や、範囲・精度・可搬性の観点も押さえ、安全に扱える実用的なサンプルコードを多数用意しました。

C言語のデータ型の基本

データ型とは何か

データ型とは、変数が取りうる値の種類(集合)と、その表現方法、演算の意味を定める約束です。

例えばintは整数、doubleは実数(浮動小数点)を扱います。

型はメモリサイズ・値の範囲・丸めの規則・演算結果に影響します。

正しく型を選ぶことは、オーバーフローや精度不足といった不具合の防止につながります。

C言語の型の分類(整数/浮動小数点/文字/論理/列挙)

C言語の基本カテゴリは次の通りです。

実装が提供する拡張を除けば、この分類で十分網羅できます。

  • 整数型: shortintlonglong long、およびunsigned
  • 文字型: charsigned charunsigned char
  • 浮動小数点型: floatdoublelong double
  • 論理型: _Bool(stdbool.hbool)
  • 列挙型: enumで定義する離散値
  • 固定幅整数: stdint.hint32_tなど(可搬性重視)
  • サイズ関連: size_tptrdiff_t

分類だけでなくどういう用途に適しているかが重要です。

後半の選び方ガイドで具体的な判断基準を示します。

サイズと値の範囲は実装依存

型のサイズや値の範囲は処理系(コンパイラとCPU/OS)に依存します。

標準が保証するのは最低限の範囲で、実際のビット幅は環境により異なります。

次は標準が保証する下限の一例です。

最低ビット数(標準規定)備考
char8以上CHAR_BITで決まる
short16以上
int16以上
long32以上
long long64以上C99以降
float実装依存多くはIEEE 754単精度
double実装依存多くはIEEE 754倍精度
long double実装依存x86拡張80bitや倍精度同等など

正確なサイズや範囲はsizeoflimits.h/float.hで毎回確認しましょう。

基本データ型の種類と値の範囲

整数型(int/short/long/long long)の違いと範囲

整数型は符号あり符号なしがあり、前者は負数を扱えます。

後者は0以上のみですが、同じビット幅なら上限値が約2倍になります。

よくある環境での典型サイズ(参考)

環境shortintlonglong long
32bit(ILP32)16323264
64bit(LP64, Linux/macOS)16326464
64bit(LLP64, Windows)16323264

単位はビットです。

実際にはsizeofで確認します。

範囲とサイズを調べるサンプル

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

int main(void) {
    // サイズはバイト、範囲はlimits.hのマクロで取得します
    printf("short:      %zu bytes, [%d, %d]\n", sizeof(short), SHRT_MIN, SHRT_MAX);
    printf("int:        %zu bytes, [%d, %d]\n", sizeof(int), INT_MIN, INT_MAX);
    printf("long:       %zu bytes, [%ld, %ld]\n", sizeof(long), LONG_MIN, LONG_MAX);
    printf("long long:  %zu bytes, [%lld, %lld]\n", sizeof(long long), LLONG_MIN, LLONG_MAX);

    // 符号なしの上限も確認
    printf("unsigned int max: %u\n", UINT_MAX);
    return 0;
}

実行結果(一例):

txt
short:      2 bytes, [-32768, 32767]
int:        4 bytes, [-2147483648, 2147483647]
long:       8 bytes, [-9223372036854775808, 9223372036854775807]
long long:  8 bytes, [-9223372036854775808, 9223372036854775807]
unsigned int max: 4294967295

この出力はLP64(多くのLinux/macOS)の例です。

Windows(LLP64)ではlongは4バイトになります。

注意: 符号あり整数のオーバーフローは未定義動作です。

例えばINT_MAX + 1は未定義になります。

加算前にif (a > INT_MAX - b)のようにチェックしましょう。

文字型(char)の役割と範囲

charは1バイト単位の文字/バイト格納に使いますが、符号付きか符号なしであるかは実装依存です。

文字コードは環境により異なります(UTF-8の各バイトを扱う場合はunsigned charが安全です)。

用途符号備考
char文字/バイト実装依存signedまたはunsigned
signed char明示的に負数ありの1バイト符号あり範囲はSCHAR_MIN..SCHAR_MAX
unsigned char生データ/バッファ符号なし0..UCHAR_MAX

バイナリ処理や文字列の各バイトを扱うときはunsigned charを使うと予期せぬ負値化を避けられます。

浮動小数点型(float/double/long double)の精度と範囲

多くの実装はIEEE 754に準拠します。

代表的な有効桁数はfloatで約7桁、doubleで約15〜16桁です。

long doubleは実装により倍精度と同等または80bit拡張精度等になります。

C言語
#include <stdio.h>
#include <float.h> // FLT_MAX, DBL_DIG など
#include <math.h>
#include <stdbool.h>

int main(void) {
    printf("float:       digits=%d,  range=[%e, %e]\n", FLT_DIG, FLT_MIN, FLT_MAX);
    printf("double:      digits=%d,  range=[%e, %e]\n", DBL_DIG, DBL_MIN, DBL_MAX);
    printf("long double: digits=%d,  range=[%Le, %Le]\n", LDBL_DIG, LDBL_MIN, LDBL_MAX);

    // 浮動小数点の丸め誤差例
    double x = 0.1 + 0.2;
    printf("0.1 + 0.2 = %.17f\n", x);
    printf("x == 0.3 ? %s\n", (x == 0.3) ? "true" : "false");
    return 0;
}

実行結果(一例):

実行結果
float:       digits=6,  range=[1.175494e-38, 3.402823e+38]
double:      digits=15,  range=[2.225074e-308, 1.797693e+308]
long double: digits=18,  range=[3.362103e-4932, 1.189731e+4932]
0.1 + 0.2 = 0.30000000000000004
x == 0.3 ? false

浮動小数点では「等しい」比較を避け、許容誤差(イプシロン)を用いた比較を検討してください。

論理型(_Boolとstdbool.hのbool)

C99以降、_Boolが導入されました。

stdbool.hをインクルードするとbool/true/falseが使えます。

論理値の表示は整数(0/1)として行います。

C言語
#include <stdio.h>
#include <stdbool.h>

int main(void) {
    bool a = true;
    bool b = (5 > 3); // 比較演算子の結果はint(0または1)だがboolに自動変換される
    printf("a=%d, b=%d, a && b=%d\n", a, b, a && b);
    return 0;
}
実行結果
a=1, b=1, a && b=1

列挙型(enum)の使いどころと注意点

enumは関連する離散値(状態や曜日など)を名前付きで表現できます。

基になる型は整数型で、値の範囲チェックは行われません

型安全性は限定的なので、外部とのデータ交換やABIでは整数幅に注意します。

C言語
#include <stdio.h>

enum Status {
    STATUS_OK = 0,
    STATUS_WARN = 1,
    STATUS_ERR = 2,
    // 途中から値を指定しないと前の+1が自動付与されます
    STATUS_FATAL // 3
};

int main(void) {
    enum Status st = STATUS_WARN;
    printf("Status=%d\n", st); // %dで印字(基になる型は実装依存だが多くはint)
    // 注意: 列挙型に範囲外の整数を代入できてしまう実装もあります
    st = 100; // コンパイラは警告する場合あり
    printf("Status(after invalid)=%d\n", st);
    return 0;
}

実行結果(一例):

実行結果
Status=1
Status(after invalid)=100

使いどころは状態管理や分岐の可読性向上です。

外部I/Oやシリアライズでは固定幅整数に写像するのが無難です。

固定幅整数(stdint.hのint32_tなど)の範囲

stdint.hはビット幅が明確な整数を提供します。

可搬性と外部仕様との整合に優れ、ネットワーク/ファイルフォーマットに適します。

  • 厳密幅: int8_tint16_tint32_tint64_tとそのuint*_t
  • 最小幅: int_least32_tなど(少なくともNビット)
  • 高速幅: int_fast32_tなど(その環境で最速)
C言語
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h> // PRIu32 などの書式マクロ

int main(void) {
    printf("int32_t:  size=%zu, min=%" PRId32 ", max=%" PRId32 "\n",
           sizeof(int32_t), INT32_MIN, INT32_MAX);
    printf("uint64_t: size=%zu, max=%" PRIu64 "\n",
           sizeof(uint64_t), UINT64_MAX);
    return 0;
}

実行結果(一例):

実行結果
int32_t:  size=4, min=-2147483648, max=2147483647
uint64_t: size=8, max=18446744073709551615

サイズ関連の型(size_t/ptrdiff_t)の用途

size_tオブジェクトの大きさや配列の添字に使われる符号なし整数です。

ptrdiff_t同じ配列内のポインタ差を表す符号あり整数です。

どちらも環境によりビット幅が変わります。

C言語
#include <stdio.h>
#include <stddef.h> // size_t, ptrdiff_t

int main(void) {
    int a[10];
    size_t n = sizeof(a) / sizeof(a[0]); // 配列要素数
    ptrdiff_t diff = &a[9] - &a[2];      // 要素間の距離(単位はint要素)
    printf("size_t: %zu (array length)\n", n);  // %zuはsize_t用
    printf("ptrdiff_t: %td (elements apart)\n", diff); // %tdはptrdiff_t用
    return 0;
}
実行結果
size_t: 10 (array length)
ptrdiff_t: 7 (elements apart)

データ型の選び方ガイド

初心者の基本方針(intとdoubleから始める)

最初は整数はint、小数はdoubleを原則にすると混乱が少ないです。

必要になったらlong longuint32_t等へ置き換えるとよいでしょう。

範囲で選ぶ(最大値/最小値とオーバーフロー回避)

扱う値の最大最小を見積もり、上限に十分な余裕を持つ型を選びます。

計算ではオーバーフロー前にチェックします。

C言語
#include <stdio.h>
#include <limits.h>
#include <stdbool.h>

bool safe_add_int(int a, int b, int *out) {
    if ((b > 0 && a > INT_MAX - b) || (b < 0 && a < INT_MIN - b)) {
        return false; // オーバーフロー
    }
    *out = a + b;
    return true;
}

int main(void) {
    int r;
    if (safe_add_int(INT_MAX, 1, &r)) {
        printf("sum=%d\n", r);
    } else {
        printf("overflow detected\n");
    }
    return 0;
}
実行結果
overflow detected

精度で選ぶ(小数はdouble、金額は整数)

小数の計算は原則doubleを使います。

一方、金額は小数で持たず、最小通貨単位(例: 円、または1/100ドル)の整数で扱うのが安全です。

税計算や累積誤差を避けられます。

パフォーマンスとメモリのバランス

現代のCPUではネイティブ幅(多くはintint32_t)が最速で、shortは必ずしも高速ではありません。

大量データではメモリ節約が利くuint16_t等が有効な場合もありますが、演算は一旦広い型に昇格してから行うなどの工夫が必要です。

可搬性を優先するなら固定幅整数を使う

外部仕様(ネットワーク/ファイル/プロトコル)やABI整合が必要な場面ではint32_t/uint64_tなど固定幅整数を選び、入出力は<inttypes.h>の書式マクロ(PRId32等)を使います。

APIや外部仕様の型に合わせる

標準ライブラリ/OSのAPIに合わせて要求される型をそのまま使うのが基本です。

例としてmallocのサイズはsize_t、ポインタ差はptrdiff_t、時間はtime_t、ソケット/プロトコルはuint32_t等が指定されます。

値の範囲とサイズの調べ方

sizeofでバイト数を確認する

sizeofは型や式のバイト数を返します。

配列の要素数計算にもよく使います。

C言語
#include <stdio.h>
#include <stdint.h>

#define COUNT_OF(a) (sizeof(a) / sizeof((a)[0])) // 静的配列の要素数

int main(void) {
    printf("sizeof(int)        = %zu\n", sizeof(int));
    printf("sizeof(long)       = %zu\n", sizeof(long));
    printf("sizeof(void*)      = %zu\n", sizeof(void*));
    printf("sizeof(int32_t)    = %zu\n", sizeof(int32_t));

    int arr[100];
    printf("elements in arr    = %zu\n", COUNT_OF(arr));
    return 0;
}

実行結果(一例):

実行結果
sizeof(int)        = 4
sizeof(long)       = 8
sizeof(void*)      = 8
sizeof(int32_t)    = 4
elements in arr    = 100

limits.hとfloat.hで最大値/最小値を得る

整数はlimits.h、浮動小数点はfloat.hが公式の情報源です。

C言語
#include <stdio.h>
#include <limits.h>
#include <float.h>

int main(void) {
    printf("INT_MIN=%d, INT_MAX=%d, UINT_MAX=%u\n", INT_MIN, INT_MAX, UINT_MAX);
    printf("DBL_MIN=%e, DBL_MAX=%e, DBL_DIG=%d\n", DBL_MIN, DBL_MAX, DBL_DIG);
    return 0;
}

実行結果(一例):

実行結果
INT_MIN=-2147483648, INT_MAX=2147483647, UINT_MAX=4294967295
DBL_MIN=2.225074e-308, DBL_MAX=1.797693e+308, DBL_DIG=15

_Static_assertで前提条件をチェックする

コンパイル時に前提(例えばビット幅)を満たすか検証できます。

C11の_Static_assert、またはassert.hstatic_assert(C11)を使います。

C言語
#include <stdint.h>
#include <assert.h> // C11以降でstatic_assertマクロが利用可能

// いずれかを使用: _Static_assert(条件, "メッセージ");
_Static_assert(sizeof(int32_t) == 4, "int32_t must be 4 bytes");
// または
static_assert(sizeof(void*) == 8, "This code assumes 64-bit pointers");

int main(void) {
    return 0;
}

このコードは条件を満たさない環境ではコンパイルエラーになります。

実行環境(32bit/64bit)の違いに注意

同じ「64bit OS」でもデータモデルが異なります。

LP64とLLP64の差は特に重要です。

データモデルintlongvoid*主な環境
ILP3232323232bit各種
LP64326464Linux, macOS(64bit)
LLP64323264Windows(64bit)

環境検出の例です。

C言語
#include <stdio.h>
#include <stdint.h>

int main(void) {
    printf("sizeof(void*)=%zu\n", sizeof(void*));
#if INTPTR_MAX == INT64_MAX
    printf("Pointer: 64-bit\n");
#elif INTPTR_MAX == INT32_MAX
    printf("Pointer: 32-bit\n");
#else
    printf("Pointer: unknown width\n");
#endif

    // データモデルの一端を表示
    printf("sizeof(int) = %zu, sizeof(long) = %zu, sizeof(long long) = %zu\n",
           sizeof(int), sizeof(long), sizeof(long long));
    return 0;
}

実行結果(一例):

実行結果
sizeof(void*)=8
Pointer: 64-bit
sizeof(int) = 4, sizeof(long) = 8, sizeof(long long) = 8

Windows 64bitではlongが4バイトである点に注意してください。

まとめ

本記事では、C言語のデータ型を分類から実装依存の注意、範囲・精度・可搬性の観点、そして目的に応じた選び方まで体系的に解説しました。

基本は整数はint、小数はdouble、外部仕様や可搬性重視ならstdint.hの固定幅整数、サイズやポインタ差にはsize_t/ptrdiff_tを用いるのが堅実です。

実装依存の違いはsizeoflimits.h/float.h_Static_assertで常に確認し、オーバーフローや丸め誤差を前提に安全なコードを書く習慣を身につけましょう。

この記事を書いた人
エーテリア編集部
エーテリア編集部

プログラミングの基礎をしっかり学びたい方向けに、C言語の基本文法から解説しています。ポインタやメモリ管理も少しずつ理解できるよう工夫しています。

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

URLをコピーしました!