HOWTO · C
C 言語で typedef enum を使用する方法
この記事では、C 言語で typedef enum を使用する方法を紹介し、その利点を説明するとともに、わかりやすい例を示します。typedef enum でコードの可読性と保守性を高める方法を学び、struct での使い方も理解できます。初心者の方も、スキルを磨きたい方も、このガイドで C の enum を簡単に扱えるようになります。
このページの内容
C 言語では、enum は名前付きの整数定数を宣言し、typedef はその enum 型に短い名前を与えることができます。名前を使うことで状態や選択肢の値を読みやすくし、入力や保存されたデータがプログラムの外部から来る可能性がある場所では、それらの値を明示的に扱います。enum は明瞭さを高めますが、変数に代入されるすべての値を自動的に検証するわけではありません。
enum を定義して値を割り当てる
明示的な値を持たない列挙子は 0 から始まります。各後続の列挙子は、前の値より 1 大きくなります。例えば、enum { MONDAY, TUESDAY, WEDNESDAY }; は 0、1、2 を割り当てます。既存の形式、プロトコル、または安定したコードの集合に合わせる必要がある場合は、値を明示的に割り当てることができます: enum Status { STATUS_READY = 1, STATUS_RUNNING = 4, STATUS_DONE = 8 };。明示的な値の後の次の暗黙の値は、その値より 1 大きくなります。
STATUS_READY のような名前は整数定数です。変数ではありません。enum 変数には後から別の値を代入でき、C は通常、その宣言に列挙された列挙子だけに制限しません。入力、ファイル、その他の外部ソースからの値は、使用する前に確認が必要なデータとして扱ってください。
enum の typedef エイリアスを作成する
enum タグと typedef エイリアスは関連していますが、異なる名前です。enum State { ... }; の場合、宣言では enum State を使います。typedef を使うと、より短いエイリアス State を導入でき、State current; のような宣言が可能になります。エイリアスは同じ型を指します。typedef は別の型を作成しません。組み合わされた書き方 typedef enum State { ... } State; は、タグとエイリアスを一緒に宣言します。
この例では、整数の入力を求め、読み取りが成功したかを確認し、その値を State に変換して Job のフィールドに格納する前に検証します。成功するパスを見るには 2 を入力してください。プログラムはプロンプトとエコーされた値を同じ行に出力し、続いて switch が Running を出力します。読み取りに失敗した場合、または数値がサポートされる 3 つの状態の範囲外である場合、プログラムはメッセージを出力し、変換の前に停止します。
#include <stdio.h>
typedef enum State {
STATE_READY = 1,
STATE_RUNNING = 2,
STATE_DONE = 3
} State;
typedef struct Job {
State state;
} Job;
int main(void) {
int input;
printf("Input: ");
if (scanf("%d", &input) != 1) {
puts("Could not read an integer.");
return 0;
}
printf("%d\n", input);
if (input != STATE_READY && input != STATE_RUNNING && input != STATE_DONE) {
puts("Unsupported state.");
return 0;
}
Job job = { (State)input };
switch (job.state) {
case STATE_READY:
puts("Ready");
break;
case STATE_RUNNING:
puts("Running");
break;
case STATE_DONE:
puts("Done");
break;
default:
puts("Unsupported state.");
break;
}
return 0;
}
Input: Could not read an integer.
struct で enum 型を使用する
上記の Job struct は、その状態を State フィールドに格納します。これにより、状態は job を説明する他のデータの隣に保持され、enum 定数によって代入や switch のケースが読みやすくなります。typedef エイリアスを定義しない場合は、フィールドをタグを使って enum State state; と宣言することもできます。
struct はフィールドに検証を追加しません。値がサポートされていない数値を提供する可能性のあるソースから来る場合は、それに頼る前に確認してください。default 分岐により、格納された値が名前付きの状態のいずれとも一致しない場合でも、プログラムに定義された応答が与えられます。特定のアプリケーションでより厳密な保証が必要な場合は、値がプログラムに入る境界で検証し、その確認を代入の近くに保持してください。
enum の値を選択し、安全に使用する
定数が内部ラベルであり、その数値表現が重要でない場合は、暗黙の値を使用してください。別のインターフェースが特定の数値を必要とする場合は、値を明示的に割り当て、データが保存または交換される場合は、それらの割り当てを安定させてください。いずれの場合も、名前付き定数は説明のない整数リテラルよりも意図をよく伝え、保守担当者が利用可能なラベルを確認できる場所を 1 つ提供します。
これらの利点により、enum 変数が読み取り専用になるわけではなく、名前付き列挙子だけを含むことが保証されるわけでもありません。変数は実行中に変化する可能性があり、キャストや外部入力によって対応する名前を持たない値が生成されることもあります。default 分岐のある switch を使用するか、値を処理する前に受け入れられる列挙子と照らして検証してください。これにより、予期しないデータが既知の状態向けのパスを静かにたどることを防げます。
要するに、名前付き定数のために enum を宣言し、宣言が明確になる場合は typedef エイリアスを追加し、struct フィールドや関数パラメーターを含め、状態が属する場所ではどこでも結果の型を使用してください。明示的な数値は、その数値が要件の一部である場合にのみ選択し、検証は enum が提供する命名の利便性とは別に保持してください。