列挙型(enum)
関連する定数に名前を付けてまとめるデータ型です。曜日・方向・状態コードなど、限られた値のセットを整数の代わりに意味のある名前で表現できます。
構文
// 列挙型を定義します(先頭から 0, 1, 2 ... の値が割り当てられます)。
enum 列挙型タグ名 {
列挙定数1,
列挙定数2,
列挙定数3
};
// 値を明示的に指定できます。
enum 列挙型タグ名 {
定数1 = 値1,
定数2 = 値2,
定数3 // 前の値 + 1 が割り当てられます。
};
// 列挙型変数を宣言します。
enum 列挙型タグ名 変数名;
enum 列挙型タグ名 変数名 = 列挙定数;
// typedef と組み合わせて enum を省略できます。
typedef enum { 定数1, 定数2 } 型名;
列挙型の特徴
| 特徴 | 概要 |
|---|---|
| デフォルト値 | 最初の定数は0から始まり、以降は1ずつ増加します。任意の位置で値を指定すると、そこから再度連番が続きます。 |
| 内部表現 | 列挙定数は実際には整数(int)として扱われます。整数との変換は暗黙的に行われます。 |
| スコープ | C言語の列挙定数はグローバルスコープに定義されます。名前の衝突を避けるため、接頭辞を付ける慣習があります。 |
| switch との相性 | 列挙型の値は整数のため、switch文と組み合わせて各状態の処理を分岐させるのが一般的です。 |
サンプルコード
sample_enum.c
#include <stdio.h>
/* 曜日を表す列挙型です(MON = 0, TUE = 1, ...)。 */
typedef enum {
MON, TUE, WED, THU, FRI, SAT, SUN
} Weekday;
typedef enum {
DIR_NORTH = 0,
DIR_EAST = 1,
DIR_SOUTH = 2,
DIR_WEST = 3
} Direction;
typedef enum {
ERR_OK = 0,
ERR_NOTFOUND = -1,
ERR_DENIED = -2,
ERR_TIMEOUT = -3
} ErrorCode;
void print_weekday(Weekday day) {
const char *names[] = {"月", "火", "水", "木", "金", "土", "日"};
printf("曜日: %s曜日\n", names[day]);
}
const char *direction_name(Direction d) {
switch (d) {
case DIR_NORTH: return "北";
case DIR_EAST: return "東";
case DIR_SOUTH: return "南";
case DIR_WEST: return "西";
default: return "不明";
}
}
int main(void) {
Weekday today = WED;
print_weekday(today);
/* 列挙型は整数として扱えます。 */
printf("水曜日の値: %d\n", WED);
Direction dir = DIR_NORTH;
printf("方向: %s\n", direction_name(dir));
ErrorCode err = ERR_NOTFOUND;
switch (err) {
case ERR_OK: printf("成功\n"); break;
case ERR_NOTFOUND: printf("見つかりませんでした。\n"); break;
case ERR_DENIED: printf("アクセスが拒否されました。\n"); break;
case ERR_TIMEOUT: printf("タイムアウトしました。\n"); break;
}
printf("Weekday のサイズ: %zu バイト\n", sizeof(Weekday));
return 0;
}
コンパイルして実行すると次のようになります。
gcc enum.c -o enum ./enum 曜日: 水曜日 水曜日の値: 2 方向: 北 見つかりませんでした。 Weekday のサイズ: 4 バイト
ゲームの状態管理に使う
ゲームやアプリケーションの状態(起動中・一時停止・終了など)を enum で管理すると、マジックナンバーを使わずにコードが読みやすくなります。
enum_game_state.c
#include <stdio.h>
typedef enum {
STATE_TITLE,
STATE_PLAYING,
STATE_PAUSED,
STATE_GAME_OVER,
STATE_COUNT /* 状態数を自動的に取得するための番兵 */
} GameState;
const char *state_name(GameState s) {
const char *names[STATE_COUNT] = {
"タイトル", "ゲーム中", "ポーズ", "ゲームオーバー"
};
return (s < STATE_COUNT) ? names[s] : "不明";
}
void transition(GameState *current, GameState next) {
printf("%s → %s\n", state_name(*current), state_name(next));
*current = next;
}
int main(void) {
GameState state = STATE_TITLE;
printf("状態数: %d\n", STATE_COUNT);
transition(&state, STATE_PLAYING);
transition(&state, STATE_PAUSED);
transition(&state, STATE_PLAYING);
transition(&state, STATE_GAME_OVER);
return 0;
}
コンパイルして実行すると次のようになります。
gcc enum_game_state.c -o enum_game_state ./enum_game_state 状態数: 4 タイトル → ゲーム中 ゲーム中 → ポーズ ポーズ → ゲーム中 ゲーム中 → ゲームオーバー
よくあるミス
よくあるミス: 異なる列挙型の定数名が衝突する
C言語の列挙定数はグローバルスコープに置かれます。異なる列挙型で同じ名前を使うとコンパイルエラーになります。接頭辞で名前空間を分けてください。
enum_naming_ng.c
#include <stdio.h>
/* NG: 接頭辞なしで定義すると同名の定数が衝突する */
/* typedef enum { NORTH, EAST, SOUTH, WEST } Direction; */
/* typedef enum { NORTH, SOUTH } Axis; */ /* NORTH が重複してコンパイルエラー */
/* OK: 接頭辞で区別する */
typedef enum {
DIR_NORTH, DIR_EAST, DIR_SOUTH, DIR_WEST
} Direction;
typedef enum {
AXIS_NORTH, AXIS_SOUTH
} Axis;
int main(void) {
Direction d = DIR_NORTH;
Axis a = AXIS_SOUTH;
printf("Direction: %d\n", d); /* 0 */
printf("Axis: %d\n", a); /* 1 */
return 0;
}
修正後は次の通りです。
gcc enum_naming_ng.c -o enum_naming_ng ./enum_naming_ng Direction: 0 Axis: 1
概要
列挙型は可読性を高めるための機能ですが、内部的には整数型です。型チェックはC言語では緩く、別の列挙型の値を誤って代入してもコンパイラは警告を出さないことがあります。列挙定数の名前はグローバルスコープに置かれるため、異なる列挙型で同じ名前を使うとコンパイルエラーになります。接頭辞(例:『DIR_』『ERR_』)で衝突を避けてください。
列挙型と『switch』を組み合わせる際、コンパイラによっては未処理の列挙定数に対して警告を出す設定があります。この警告を活用すると、定数の追加漏れを防げます。
関連する定数のグループは列挙型で、異なる型のデータをまとめる場合は『struct(構造体)』を使います。型名の別名については『typedef』も参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。