列挙ステートメントと定義ステートメントの違い
-
02-07-2019 - |
質問
C / C ++でdefineステートメントとenumステートメントを使用することの違いは何ですか(また、CまたはC ++で使用すると違いがありますか?)
たとえば、いつ使用すべきか
enum {BUFFER = 1234};
オーバー
#define BUFFER 1234
解決
enum
は構文要素を定義します。
#define
はプリコンパイラ指令で、コンパイラがコードを見る前に 実行されるため、C自体の言語要素ではありません。
一般に列挙型は、タイプセーフであり、より簡単に検出できるため、推奨されます。定義は見つけるのが難しく、複雑な動作をすることがあります。たとえば、あるコードは別のコードによって作成された #define
を再定義できます。これを追跡するのは難しい場合があります。
他のヒント
#define
ステートメントは、コンパイラがコードを見る前にプリプロセッサによって処理されるため、基本的にはテキストの置換になります(実際には、パラメータなどを使用することでもう少しインテリジェントになります)。
列挙型はC言語自体の一部であり、次の利点があります。
1 /型を持っている可能性があり、コンパイラはそれらを型チェックできます。
2 /コンパイラで使用できるため、それらのシンボル情報をデバッガに渡すことができ、デバッグが容易になります。
定義はプリプロセッサコマンドです。「すべて置換」を実行するのと同じです。エディタで、文字列を別の文字列に置き換えてから結果をコンパイルできます。
Enumは、たとえば次のように記述する場合の型の特殊なケースです。
enum ERROR_TYPES
{
REGULAR_ERR =1,
OK =0
}
ERROR_TYPESという新しいタイプが存在します。 REGULAR_ERRは1になりますが、この型からintへのキャストは、キャスト警告を生成する必要があります(コンパイラを高冗長性に設定した場合)。
概要: どちらも似ていますが、enumを使用する場合は型チェックを利用し、定義を使用することでコード文字列を置き換えるだけです。
通常、列挙型を使用することが理にかなっている場合は、#defineよりも列挙型が好まれます。
- デバッガは、
enum
の値のシンボル名("openType:2 ではなく、"
openType:OpenExisting
"を表示できます。 code>" - 名前の衝突からもう少し保護されますが、これはそれほど悪くはありません(ほとんどのコンパイラはre
#define
itionについて警告します。
最大の違いは、列挙型を型として使用できることです:
// Yeah, dumb example
enum OpenType {
OpenExisting,
OpenOrCreate,
Truncate
};
void OpenFile(const char* filename, OpenType openType, int bufferSize);
これにより、パラメータの型チェックが可能になり(openTypeとbufferSizeを簡単に混同することはできません)、有効な値を簡単に見つけることができ、インターフェースがはるかに使いやすくなります。一部のIDEでは、 intellisense コード補完を行うこともできます!
可能な場合は常に列挙型を使用することをお勧めします。列挙型を使用すると、ソースコードに関するより多くの情報がコンパイラに提供されます。プリプロセッサ定義はコンパイラによって認識されることはないため、伝達される情報が少なくなります。
たとえば多数のモードで、列挙型を使用すると、コンパイラーはスイッチ内の欠落している case
-statementsなどをキャッチできます。
enumは複数の要素を1つのカテゴリにグループ化できます:
enum fruits{ apple=1234, orange=12345};
#defineは無関係な定数のみを作成できます:
#define apple 1234
#define orange 12345
#defineはプリプロセッサコマンドです。enumはCまたはC ++言語です。
この種のケースでは、常に#defineよりも列挙型を使用することをお勧めします。一つのことは型安全です。もう1つは、値のシーケンスがある場合、enumでシーケンスの先頭を指定するだけで、他の値は連続した値を取得することです。
enum {
ONE = 1,
TWO,
THREE,
FOUR
};
の代わりに
#define ONE 1
#define TWO 2
#define THREE 3
#define FOUR 4
補足として、#defineを使用しなければならない場合もあります(通常、ある種のマクロでは、定数を含む識別子を作成する必要がある場合)が、マクロ黒魔術であり、行く方法としては非常にまれです。これらの端に行く場合は、おそらくC ++テンプレートを使用する必要があります(ただし、C ...にこだわっている場合)。
この単一の定数(buffersizeなど)のみが必要な場合は、列挙型ではなく定義を使用します。戻り値(エラー状態が異なることを意味する)のようなものや、異なる「タイプ」を区別する必要がある場所には列挙型を使用します。または「ケース」。その場合、enumを使用して、関数プロトタイプなどで使用できる新しい型を作成できます。その後、コンパイラはそのコードをより適切にチェックできます。
すでに書かれているすべてのことに加えて、ある人は言ったが、示されていないので、代わりに興味深い。例:
enum action { DO_JUMP, DO_TURNL, DO_TURNR, DO_STOP };
//...
void do_action( enum action anAction, info_t x );
アクションを型として考慮すると、物事が明確になります。 defineを使用すると、次のように記述できます
void do_action(int anAction, info_t x);
整数定数値については、 #define
よりも enum
を好むようになりました。 enum
を使用することにはデメリットはないようです(もう少し入力することの極度のデメリットを割引きます)が、 enum
をスコープできるという利点があります。 #define 識別子には、すべてを処理するグローバルスコープがあります。
#define
を使用することは通常問題ではありませんが、 enum
に欠点はないので、私はそれに取り組みます。
C ++では、 const int
をリテラル整数の代わりに使用できる場合でも、一般的に const int
よりも enum
を好む enum
はCに移植可能であるため(Cの場合とは異なります)(まだ多くの作業をしています)。
定数のグループ(「Days of the Week」など)がある場合、enumはグループ化されていることを示すため、望ましいでしょう。そして、ジェイソンが言ったように、それらはタイプセーフです。グローバル定数(バージョン番号など)の場合は、 #define
を使用する方がより適切です。ただし、これは多くの議論の対象となっています。
上記の優れた点に加えて、enumのスコープをクラス、構造体、または名前空間に制限できます。個人的に、一度にスコープ内に最小数の関連シンボルを持つことが好きです。これは、#definesではなくenumを使用するもう1つの理由です。
定義のリストに対する列挙のもう1つの利点は、コンパイラー(少なくともgcc)がswitchステートメントですべての値がチェックされない場合に警告を生成できることです。例:
enum {
STATE_ONE,
STATE_TWO,
STATE_THREE
};
...
switch (state) {
case STATE_ONE:
handle_state_one();
break;
case STATE_TWO:
handle_state_two();
break;
};
前のコードでは、コンパイラは、列挙型のすべての値がスイッチで処理されるわけではないという警告を生成できます。状態が#defineとして行われた場合、これは当てはまりません。
enumsは、1週間の日数など、ある種のセットを列挙するために使用されます。定数が1つだけ必要な場合は、 const int
(またはdoubleなど)がenumよりも明確に優れています。私は個人的に #define
が好きではありません(少なくともいくつかの定数の定義に関しては)それは私にタイプセーフを与えないからです。 >
enumを作成すると、リテラルだけでなく、これらのリテラルをグループ化する型も作成されます。これにより、コンパイラーがチェックできるコードにセマンティックが追加されます。
さらに、デバッガーを使用する場合、enumリテラルの値にアクセスできます。これは、常に#defineに当てはまるわけではありません。
上記のいくつかの答えはさまざまな理由で列挙型を使用することを推奨していますが、定義を使用することはインターフェースを開発するときに実際の利点があることを指摘したいと思います。新しいオプションを導入し、ソフトウェアにそれらを条件付きで使用させることができます。
例:
#define OPT_X1 1 /* introduced in version 1 */ #define OPT_X2 2 /* introduced in version 2 */
次に、どちらのバージョンでもコンパイルできるソフトウェア
#ifdef OPT_X2 int flags = OPT_X2; #else int flags = 0; #endif
列挙では、これはランタイム機能検出メカニズムなしでは不可能です。
列挙:
1。通常、複数の値に使用されます
2。 enumには、nameとnameの値を区別する必要がありますが、値は同じである必要があります。値を定義しない場合、enum nameの最初の値は0、2番目の値は明示的に値が指定されていない限り、1など。
3。型を持っている可能性があり、コンパイラはそれらを型チェックできます
4。デバッグを簡単にする
5。スコープをクラスに制限できます。
定義:
1。 1つの値のみを定義する必要がある場合
2。通常、ある文字列を別の文字列に置き換えます。
3。スコープはグローバルです。スコープを制限することはできません
全体として列挙型を使用する必要があります
ほとんど違いはありません。 C標準では、列挙型は整数型であり、列挙定数はint型であるため、両方ともエラーなしで他の整数型と自由に混在させることができます。 (一方で、明示的なキャストなしでそのような混合が許可されない場合、列挙の賢明な使用は特定のプログラミングエラーをキャッチできます。)
列挙のいくつかの利点は、数値が自動的に割り当てられること、列挙変数が検査されるときにデバッガーがシンボリック値を表示できること、およびブロックスコープに従うことです。 (列挙が無差別に混在している場合、コンパイラは致命的でない警告を生成することもあります。厳密に違法ではない場合でも、そうするとスタイルが悪いと見なされる可能性があるためです。)欠点は、プログラマが致命的でない警告をほとんど制御できないことです一部のプログラマは、列挙変数のサイズを制御できないことにnotします。