enum - 列挙型
列挙型enumを使うと、連続した整数定数を簡単に記述できます。定数名1は0、定数名2は1、定数名3は2になります。
enum { 定数名1, 定数名2, 定数名3, ... };
列挙型enumを使ったサンプルです。enumは、switch文と組み合わせて使うことが多いので、switch文のサンプルにしました。
#include <stdint.h> #include <stdio.h> enum { SPVM_OP_C_ID_IF, SPVM_OP_C_ID_UNLESS, SPVM_OP_C_ID_ELSIF, SPVM_OP_C_ID_ELSE, SPVM_OP_C_ID_FOR, }; int main(void) { int32_t id = SPVM_OP_C_ID_UNLESS; switch (id) { case SPVM_OP_C_ID_IF: { printf("IF\n"); break; } case SPVM_OP_C_ID_UNLESS: { printf("UNLESS\n"); break; } case SPVM_OP_C_ID_ELSIF: { printf("ELSIF\n"); break; } case SPVM_OP_C_ID_ELSE: { printf("ELSE\n"); break; } case SPVM_OP_C_ID_FOR: { printf("FOR\n"); break; } default: { printf("No Match\n"); } } }
列挙型の識別子は、識別子名であればなんでもよいですが、C言語には名前空間がないので、サンプルでは、名前空間を持っているかのような名前「SPVM_OP_C_XXX」(spvm_op.cのソースコードの中の定数「constant」定義という意味)を使用しています。
列挙型はマクロによる定数定義と意味的に同じ
列挙型はマクロによる定数定義と意味的に同じです。
// 列挙型を使った整数定義 enum { SPVM_OP_C_ID_IF, SPVM_OP_C_ID_UNLESS, SPVM_OP_C_ID_ELSIF, } // マクロを使った整数定義 #define SPVM_OP_C_ID_IF 0 #define SPVM_OP_C_ID_UNLESS 1 #define SPVM_OP_C_ID_ELSIF 2
意味的には同じですが、列挙型は、マクロとことなりプリプロセッサではなく、コンパイラで処理されることが特徴です。コンパイラによって、定数に展開されます。
C言語のベストプラクティスは、マクロを必要な部分以外では使わないということですので、連続した整数定数が欲しい場合は、列挙型を使うのがお勧めです。
列挙型の疑問
列挙型の疑問を列挙します。
列挙型で整数の開始値を変えることはできますか?
はい、できます。整数の開始値を変えるには「定数名 = 数値」のように書きます。次の低数値は、インクリメントされた値になります。
enum { SPVM_OP_C_ID_IF = 3, # 3 SPVM_OP_C_ID_UNLESS, # 4 SPVM_OP_C_ID_ELSIF, # 5 SPVM_OP_C_ID_ELSE = 10, # 10 SPVM_OP_C_ID_FOR, # 11 };
列挙型で浮動小数点を使うことはできますか?
できません。浮動小数点の定数を定義したい場合は、マクロを使用してください。
#define SPVM_OP_C_ID_IF 5.23
列挙型の識別子は大文字ですか?
C言語の一般的な慣習として、定数は大文字で書くのがよいと思われます。
列挙型はどの辺が型なのですか?
列挙型には、型のように名前をつけて、型のように扱うことができます。
enum SPVM_OP_C_ID_TYPE { SPVM_OP_C_ID_IF, SPVM_OP_C_ID_UNLESS, SPVM_OP_C_ID_ELSIF, SPVM_OP_C_ID_ELSE, SPVM_OP_C_ID_FOR, };
ただし列挙型に代入したときの挙動はC言語仕様では定義されておらず、処理系依存ですので、型としての列挙型は、あまり使わない方がよいのではないかと考えます。
列挙型ではなく文字列を使ってはいけないのですか?
列挙型は、整数に対して識別子、つまり名前をつける機能です。それならば最初から、文字列を使ってはいけないのでしょうか?
動的な型を持つPerlのようなプログラミング言語であれば、この方法がベストだと思います。
if ($id eq 'if') { } elsif ($id eq 'unless') { }
ただしC言語を使うのであれば、パフォーマンスを速くしたいという動機を持って使っているのでしょう。識別子がある数を超える場合(10個くらい?)は、switch文で整数判定して、ジャンプするのが、最速です。
またC言語の文字列比較は、strcmp関数を使う必要がありますし「\0」で文字列が終わっておらず、意図しない領域まで読み込む可能性もあります。
このようにC言語の文字列では、考えるべきことがたくさんあるので、列挙型で整数を定義しておくのが、やりやすいです。
分割コンパイルする場合は、列挙型はどこで定義しますか?
分割コンパイルを使ったヘッダとソースコードに分けてコンパイルする場合の構成としては、列挙型はヘッダファイルに記述します。
マクロ定数や列挙型などの定数定義は、ヘッダに記述します。
spvm_op.h
enum { SPVM_OP_C_ID_IF, SPVM_OP_C_ID_UNLESS, SPVM_OP_C_ID_ELSIF, SPVM_OP_C_ID_ELSE, SPVM_OP_C_ID_FOR, };
spvm_op.c
#include <stdint.h> #include <stdio.h> #include "spvm_op.h" int main(void) { int32_t id = SPVM_OP_C_ID_UNLESS; switch (id) { case SPVM_OP_C_ID_IF: { printf("IF\n"); break; } case SPVM_OP_C_ID_UNLESS: { printf("UNLESS\n"); break; } case SPVM_OP_C_ID_ELSIF: { printf("ELSIF\n"); break; } case SPVM_OP_C_ID_ELSE: { printf("ELSE\n"); break; } case SPVM_OP_C_ID_FOR: { printf("FOR\n"); break; } default: { printf("No Match\n"); } } }