Cで列挙型(列挙型)を定義する方法は?


272

Cの列挙型を使用するための適切な構文が何かはわかりません。私は次のコードを持っています:

enum {RANDOM, IMMEDIATE, SEARCH} strategy;
strategy = IMMEDIATE;

しかし、これはコンパイルされず、次のエラーが発生します。

error: conflicting types for strategy
error: previous declaration of strategy was here

何が悪いのですか?


7
何年も前の質問です。おそらく誰もこれを見ることはないでしょう。しかし、なぜこれがエラーになるのでしょうか?私の知る限り、問題になっているように、問題なく機能するはずです。
Utkan Gezer 14

2
@Solverなぜこの構文は間違っているのですか?
MCG

6
@MCQ、ネクロされたネクロを壊滅させる:質問で提示された構文はC では間違っていません。strategy匿名の列挙型があると宣言し、その型の宣言された値の1つを割り当てます。さらに、さもなければ簡単なmain()関数で提示されたコードをラップすると、警告なしで、gcc 4.4.7を使用して問題なくコンパイルされます。あまり多くの言葉ではありませんが、回答のいくつかは同じことを意味します。
John Bollinger、2016

5
ほとんどの回答には、質問の2行のコードが単なるスニペットではないという事実が欠けています。それらはソースファイル全体です。これらの2行が関数の本体に含まれている場合、エラーは発生しません。それらが関数宣言の外側のファイルスコープに表示される場合、OPが尋ねたエラー(および私が試したときに他のいくつか)が表示されます。基本的な問題は、コンパイラがstrategy = IMMEDIATE;宣言として処理しようとしていることです。ANSI C以前では合法だった形式ですが、現代のCでは違法です。ファイルスコープでの割り当ては許可されていません。
キーストンプソン

3
@Solver:enum strategy { ... };名前付き列挙型定義enum strategystrategyタグです。enum { ... } strategy;は、匿名の列挙型(タグなし)、その型のという名前の単一のオブジェクトを定義しますstrategy。どちらも完全に合法です。彼らは単に別のことを意味します。
キース・トンプソン

回答:


377

enum変数の宣言は次のように行われます。

enum strategy {RANDOM, IMMEDIATE, SEARCH};
enum strategy my_strategy = IMMEDIATE;

ただし、次のようにa typedefを使用して変数宣言を短縮できます。

typedef enum {RANDOM, IMMEDIATE, SEARCH} strategy;
strategy my_strategy = IMMEDIATE;

タイプと変数を区別するための命名規則を持つことは良い考えです:

typedef enum {RANDOM, IMMEDIATE, SEARCH} strategy_type;
strategy_type my_strategy = IMMEDIATE;

1
しかし、OPは匿名の列挙型の変数を求めていました
osvein 2017

私は入力しませんでしたenum MyEnum {} myVar;し、その変数を使用しmyVar、次のように:myVar = SOMEENUMCONSTANT;
マッシュ状

451

必要としないことを指摘する価値ありtypedefます。次のようにそれを行うことができます

enum strategy { RANDOM, IMMEDIATE, SEARCH };
enum strategy my_strategy = IMMEDIATE;

あなたが好むかどうかはスタイルの質問ですtypedef。それがなければ、列挙型を参照したい場合は、を使用する必要がありますenum strategy。それで、あなたはただ言うことができますstrategy

どちらの方法にも長所と短所があります。1つはもっと意味がありますが、型識別子は通常の識別子と競合しない(名前struct statstat関数を考えてください。これらも競合しない)タグ型名前空間に保持され、すぐに型であることがわかります。もう1つは短いですが、型識別子を通常の名前空間に持ち込みます。


6
それが間違っているので、それは受け入れられた答えであってはなりません。enum戦略{...}は使用できません。Cでは-C ++でもできますし、そうすべきです。
2014年

19
@Clearer:このコードは完全に機能します。以下は実際の例です。ideone.com / T0YV17enum両方の行でキーワードを 使用していることに注意してください。
RichieHindle 2014年

または「typedef enum strategy {RANDOM、IMMEDIATE、SEARCH} strategy_t;」列挙型を使用する開発者は、必要な規則を使用できます。
Andy Nugent 2014年

これは優れた働きenum strategy { RANDOM, IMMEDIATE, SEARCH }; をします。次に、その列挙型のインスタンスが必要な場合: `enum strategy myEnum;
user3629249

2
@AndyNugentはそれをしないでください!* _tタイプはPOSIXによって予約されています
osvein '16

58

strategy2回宣言しようとしているため、上記のエラーが発生します。以下は問題なく動作します(でコンパイルgcc -ansi -pendantic -Wall)。

#include <stdio.h>

enum { RANDOM, IMMEDIATE, SEARCH } strategy = IMMEDIATE;

int main(int argc, char** argv){
    printf("strategy: %d\n", strategy);

    return 0;
}

上記の代わりに、2行目が次のように変更された場合:

...
enum { RANDOM, IMMEDIATE, SEARCH } strategy;
strategy = IMMEDIATE;
...

警告から、間違いを簡単に確認できます。

enums.c:5:1: warning: data definition has no type or storage class [enabled by default]
enums.c:5:1: warning: type defaults to int in declaration of strategy [-Wimplicit-int]
enums.c:5:1: error: conflicting types for strategy
enums.c:4:36: note: previous declaration of strategy was here

したがって、コンパイラはデフォルトの型でstrategy = IMMEDIATE呼び出される変数の宣言を必要とstrategyしましたintで変数の宣言を受け入れましたが、この名前の変数の以前の宣言はすでに存在していました。

ただし、割り当てをmain()関数に配置した場合、それは有効なコードになります。

#include <stdio.h>

enum { RANDOM, IMMEDIATE, SEARCH } strategy = IMMEDIATE;

int main(int argc, char** argv){
    strategy=SEARCH;
    printf("strategy: %d\n", strategy);

    return 0;
}

48

あなたが言う時

enum {RANDOM, IMMEDIATE, SEARCH} strategy;

名前のない列挙型の「戦略」と呼ばれる単一のインスタンス変数を作成します。これはあまり便利なことではありません。typedefが必要です。

typedef enum {RANDOM, IMMEDIATE, SEARCH} StrategyType; 
StrategyType strategy = IMMEDIATE;

9
なぜこれが役に立たないのですか?タイプの名前を気にしない場合、なぜ名前を付ける必要があるのですか?ここで意図されているのは変数に名前を付けることだけだったので、新しい値を割り当てることが可能です。
MSalters 2009

3
それはあまり役に立たなかったと私は言った、そしてそれは信じられない。確かに、私は自分のコードでこのパターンを使用していません。YMMV。

3
@HorseSMith名前のない列挙型は、その型の他の変数、または関数のパラメーターや戻り値を持つことができないため、あまり役に立ちません。1つの変数で十分であれば、問題ありません。
ボブスタイン

3
匿名の列挙型を使用していない誰かは、それらが使用されていないことを証明しません。typedefは必要ありません。いくつかのコードガイドライン(kernel.org/doc/Documentation/CodingStyle)でも、それを思いとどまらせます。
martinkunev 2015年

2
この回答も誤解を招くものです。タルクの答えはここで唯一正しいものです。
ナイトプール

13

書かれているように、コードに問題はありません。あなたはあなたが次のようなことをしていませんか?

int strategy;
...
enum {RANDOM, IMMEDIATE, SEARCH} strategy;

エラーメッセージはどの行を指していますか?「以前の「戦略」の宣言がここにあった」と書かれている場合、「ここ」は何で何を示しているのでしょうか。


6
彼はおそらくstrategy = IMMEDIATE;ファイルスコープでした。割り当ては、すべての関数の外側のファイルスコープでは発生しません。そのため、コンパイラーはエラーから最善を尽くそうとし、彼がを意味すると想定しましたがint strategy = IMMEDIATE;、その時点で競合が発生しました。
Johannes Schaub-litb 2009

2
これが最良の答えです。他の答えには非常に混乱があり、苦痛です。
2015

12

投稿された質問への彼のコメントで@ThoAppelsinは正しいです。質問に投稿されたコードスニペットは有効であり、エラーはありません。あなたが持っているエラーは、Cソースファイルの他の場所にある他の悪い構文が原因である必要があります。enum{a,b,c};3つのシンボリック定数(定義abおよびc値を持つ整数である)01そして2それぞれを、私たちが使用している場合enum、我々は通常、特定の整数値を気にしないので、それは我々はシンボリック定数名の意味について、より気に、です。これはあなたがこれを持つことができることを意味します:

#include <stdio.h>
enum {a,b,c};
int main(){
  printf("%d\n",b);
  return 0;
}

これが出力されます1

これも有効です:

#include <stdio.h>
enum {a,b,c};
int bb=b;
int main(){
  printf("%d\n",bb);
  return 0;
}

以前と同じように出力されます。

これを行う場合:

enum {a,b,c};
enum {a,b,c};

エラーが発生しますが、これを行うと:

enum alfa{a,b,c};
enum alfa;

エラーは発生しません。

あなたはこれを行うことができます:

enum {a,b,c};
int aa=a;

aa値を持つ整数変数になります0。これを行うこともできます:

enum {a,b,c} aa= a;

同じ効果があります(つまりaaintwith 0値です)。

これを行うこともできます:

enum {a,b,c} aa= a;
aa= 7;

と値にaaなりintます7

enum前に言ったように、を使用してシンボリック定数の定義を繰り返すことはできないため、を使用してintvars を宣言する場合は、タグを使用する必要がありますenum

enum tag1 {a,b,c};
enum tag1 var1= a;
enum tag1 var2= b;

これを使用すると、変数を定義するためにtypedef毎回書き込むことenum tag1がなくなります。typedefあなただけを入力することができますTag1

typedef enum {a,b,c} Tag1;
Tag1 var1= a;
Tag1 var2= b;

また、次のこともできます。

typedef enum tag1{a,b,c}Tag1;
Tag1 var1= a;
enum tag1 var2= b;

最後に言うと、定義されたシンボリック定数について話しているので、を使用するときは大文字を使用する方がよいenumということです。たとえば、次のようになります。

enum {A,B,C};

の代わりに

enum {a,b,c};

10

C ++では、 "enum"を使用して、typedefステートメントを必要とせずに新しい型を定義できることに言及する価値があります。

enum Strategy {RANDOM, IMMEDIATE, SEARCH};
...
Strategy myStrategy = IMMEDIATE;

このアプローチの方がはるかに親しみやすいと思います。

[編集-C ++ステータスを明確化-私はこれを最初に持っていて、それを削除しました!]


はい、C ++では列挙型(または構造体、共用体など)でtypedefを使用しないでください。

17
この質問はCに対するものであり、C ++に対するものではありません。Cでは、上記のコードは無効です-を使用typedefするかenum、変数宣言でも指定する必要があります:enum Strategy {RANDOM、IMMEDIATE、SEARCH}; ...列挙型ストラテジーmyStrategy = IMMEDIATE;
Pavel Minaev 2009

@pavel-私の悪い。私はもともと「C ++で」を持っていましたが、それと矛盾するように思われるいくつかの調査を行いました。
ロディ

@Pavelを使用するメリットを説明する別の答えになると思いますenum Strategy。私はそれをしました、以下を見てください。
Johannes Schaub-litb 2009

8

宣言について混乱があるようです。

ときにstrategy前に来て{RANDOM, IMMEDIATE, SEARCH}、以下のように

enum strategy {RANDOM, IMMEDIATE, SEARCH};

という名前の新しいタイプを作成していますenum strategy。ただし、変数を宣言するときは、enum strategyそれ自体を使用する必要があります。だけでは使えませんstrategy。したがって、以下は無効です。

enum strategy {RANDOM, IMMEDIATE, SEARCH};
strategy a;

一方、以下は有効です

enum strategy {RANDOM, IMMEDIATE, SEARCH};

enum strategy queen = RANDOM;
enum strategy king = SEARCH;
enum strategy pawn[100];

strategy{RANDOM, IMMEDIATE, SEARCH}に来ると、匿名の列挙型を作成し、strategyその型の変数であることを宣言します。

だから今、あなたは何かをすることができます

enum {RANDOM, IMMEDIATE, SEARCH} strategy;
strategy = RANDOM;

ただし、enum {RANDOM, IMMEDIATE, SEARCH}名前を付けたことがないため、他のタイプの変数を宣言することはできません。したがって、以下は無効です

enum {RANDOM, IMMEDIATE, SEARCH} strategy;
enum strategy a = RANDOM;

両方の定義を組み合わせることもできます

enum strategy {RANDOM, IMMEDIATE, SEARCH} a, b;

a = RANDOM;
b = SEARCH;
enum strategy c = IMMEDIATE;

Typedef 前述のように、短い変数宣言を作成するために使用されます。

typedef enum {RANDOM, IMMEDIATE, SEARCH} strategy;

これで、enum {RANDOM, IMMEDIATE, SEARCH}と同じ意味を持つコンパイラーを指定しましたstrategy。これでstrategy、変数型として自由に使えるようになりました。enum strategyもうタイプする必要はありません。以下は現在有効です

strategy x = RANDOM;

Typedefを列挙名と組み合わせて取得することもできます

typedef enum strategyName {RANDOM, IMMEDIATE, SEARCH} strategy;

この方法を使用する利点はstrategyenum strategyName交換して使用できるようになったことを除けば、あまりありません。

typedef enum strategyName {RANDOM, IMMEDIATE, SEARCH} strategy;

enum strategyName a = RANDOM;
strategy b = SEARCH;

1
すばらしい答えです。また、次のように記述された列挙型の定義にも遭遇しました:typedef enum strategy {RANDOM, IMMEDIATE, SEARCH} strategyまたはtypedef enum strategy {RANDOM, IMMEDIATE, SEARCH} strategy_type。それには何か利点がありtypedef enum {RANDOM, IMMEDIATE, SEARCH} strategyますか?完全にするために、これらを回答に追加することを検討しますか?
2016

はい。答えを修正しました。私の知る限りでは、一般的なケースでは大きな利点はありません。
混乱

2
すばらしい、あなたの答えは今、それをすべてカバーしています、ありがとう。残念なことに、それは回答のリストのはるか下にあります。適切な説明とともに、元の質問に明示的に対処しているためです。
2016

2

列挙型の名前を宣言すると、エラーは発生しません。

宣言されていない場合は、以下を使用する必要がありますtypedef

enum enum_name {RANDOM, IMMEDIATE, SEARCH} strategy;
strategy = IMMEDIATE;

エラーは表示されません...


2

私のお気に入りで唯一使用されている構造は常に次のとおりです。

typedef enum MyBestEnum
{
    /* good enough */
    GOOD = 0,
    /* even better */
    BETTER,
    /* divine */
    BEST
};

これはあなたが抱えている問題を取り除くと信じています。新しいタイプの使用は私の観点から正しいオプションです。


1

タルクの答えは最高です。

列挙型の議論の多くは、赤いニシンです。

このコードスニペットを比較します。

int strategy;
strategy = 1;   
void some_function(void) 
{
}

与える

error C2501: 'strategy' : missing storage-class or type specifiers
error C2086: 'strategy' : redefinition

これで問題なくコンパイルできます。

int strategy;
void some_function(void) 
{
    strategy = 1;   
}

変数 strategyは宣言時または関数内などで設定する必要があります。グローバルスコープで任意のソフトウェア(特に割り当て)を作成することはできません。

彼がintの代わりに列挙型{RANDOM、IMMEDIATE、SEARCH}を使用したという事実は、それを超えて見ることができない人々を混乱させた範囲にのみ関連しています。質問の再定義エラーメッセージは、これが作者が間違ったことを示していることを示しています。

これで、以下の例の最初の例が間違っており、他の3つは問題ない理由を確認できるはずです。

例1.間違い!

enum {RANDOM, IMMEDIATE, SEARCH} strategy;
strategy = IMMEDIATE;
void some_function(void) 
{
}

例2.正しい。

enum {RANDOM, IMMEDIATE, SEARCH} strategy = IMMEDIATE;
void some_function(void) 
{
}

例3.正しい。

enum {RANDOM, IMMEDIATE, SEARCH} strategy;
void some_function(void) 
{
    strategy = IMMEDIATE;
}

例4.正しい。

void some_function(void) 
{
    enum {RANDOM, IMMEDIATE, SEARCH} strategy;
    strategy = IMMEDIATE;
}

動作するプログラムがある場合は、これらのスニペットをプログラムに貼り付けるだけで、コンパイルできるものとできないものがあります。


0

エラーなしでコンパイルするために、gccを試してみて、必要に応じて、最後の代替手段を使用することを余儀なくされました。

typedef enum state {a = 0、b = 1、c = 2} state ;

typedef enum state {a = 0, b = 1, c = 2} state;

typedef enum state old; // New type, alias of the state type.
typedef enum state new; // New type, alias of the state type.

new now     = a;
old before  = b;

printf("State   now = %d \n", now);
printf("Sate before = %d \n\n", before);

newC ++の演算子であるため、Cファミリの識別子の選択としては不適切です。
jww

0

C

enum stuff q;
enum stuff {a, b=-4, c, d=-2, e, f=-3, g} s;

符号付き整数の仮定義として作用宣言s符号付き整数の仮定義として作用完全型と宣言とqスコープに不完全型で(タイプ定義が中に存在する任意の場所であるので範囲で完全型に解決さスコープ)(他の暫定的な定義と同様に、識別子qs同じタイプintまたはenum stuff複数回の不完全または完全なバージョンで再宣言できますが、スコープで一度だけ定義されます(つまり、int q = 3)。サブスコープでのみ再定義できます。定義後にのみ使用可能)。また、あなたは完全なタイプのenum stuff、タイプ定義として機能するため、スコープ内で 1回。

のコンパイラ列挙型定義enum stuffは、ファイルスコープ(前後で使用可能)および前方型宣言にも存在します(型にenum stuffは複数の宣言を含めることができますが、スコープ内の定義/完了は1つだけで、サブスコープで再定義できます)。 。また、現在のスコープ内で、arvalue 0bwith -4cwith 5dwith -2ewith -3fwith -1gwith -2に置き換えるコンパイラディレクティブとしても機能します。列挙定数は、定義後、同じスコープレベルにすることはできない別の列挙での次の再定義まで適用されます。

typedef enum bool {false, true} bool;

//this is the same as 
enum bool {false, true};
typedef enum bool bool;

//or
enum bool {false, true};
typedef unsigned int bool;

//remember though, bool is an alias for _Bool if you include stdbool.h. 
//and casting to a bool is the same as the !! operator 

enum、struct、およびunionによって共有されるタグ名前空間は別個であり、Cでtypeキーワード(enum、struct、またはunion)を前に付ける必要があります。つまりenum a {a} b、をenum a c使用し、は使用しないでくださいa c。タグの名前空間は識別子の名前空間とは別なので、enum a {a} b許可されていますがenum a {a, b} b、定数は変数の識別子と同じ名前空間である識別子の名前空間にあるため許可されていません。typedef enum a {a,b} btypedef-namesは識別子の名前空間の一部であるため、これも許可されていません。

のタイプenum boolと定数は、Cでは次のパターンに従います。

+--------------+-----+-----+-----+
|   enum bool  | a=1 |b='a'| c=3 |  
+--------------+-----+-----+-----+
| unsigned int | int | int | int |  
+--------------+-----+-----+-----+

+--------------+-----+-----+-----+
|   enum bool  | a=1 | b=-2| c=3 |  
+--------------+-----+-----+-----+
|      int     | int | int | int |  
+--------------+-----+-----+-----+

+--------------+-----+---------------+-----+
|   enum bool  | a=1 |b=(-)0x80000000| c=2 |
+--------------+-----+---------------+-----+
| unsigned int | int |  unsigned int | int |
+--------------+-----+---------------+-----+

+--------------+-----+---------------+-----+
|   enum bool  | a=1 |b=(-)2147483648| c=2 |
+--------------+-----+---------------+-----+
| unsigned int | int |  unsigned int | int |
+--------------+-----+---------------+-----+

+-----------+-----+---------------+------+
| enum bool | a=1 |b=(-)0x80000000| c=-2 |
+-----------+-----+---------------+------+
|    long   | int |      long     |  int |
+-----------+-----+---------------+------+

+-----------+-----+---------------+------+
| enum bool | a=1 | b=2147483648  | c=-2 |
+-----------+-----+---------------+------+
|    long   | int |      long     |  int |
+-----------+-----+---------------+------+

+-----------+-----+---------------+------+
| enum bool | a=1 | b=-2147483648 | c=-2 |
+-----------+-----+---------------+------+
|    int    | int |      int      |  int |
+-----------+-----+---------------+------+

+---------------+-----+---------------+-----+
|   enum bool   | a=1 | b=99999999999 | c=1 |
+---------------+-----+---------------+-----+
| unsigned long | int | unsigned long | int |
+---------------+-----+---------------+-----+

+-----------+-----+---------------+------+
| enum bool | a=1 | b=99999999999 | c=-1 |
+-----------+-----+---------------+------+
|    long   | int |      long     |  int |
+-----------+-----+---------------+------+

これはCでうまくコンパイルされます:

#include <stdio.h>
enum c j;
enum c{f, m} p;
typedef int d;
typedef int c;
enum c j;
enum m {n} ;
int main() {
  enum c j;
  enum d{l};
  enum d q; 
  enum m y; 
  printf("%llu", j);
}

C ++

C ++では、列挙型は型を持つことができます

enum Bool: bool {True, False} Bool;
enum Bool: bool {True, False, maybe} Bool; //error

この場合、定数と識別子はすべて同じ型のブール値を持ち、数値をその型で表すことができない場合はエラーが発生します。多分= 2、これはブールではありません。また、True、False、Boolを小文字にすることはできません。そうしないと、言語のキーワードと競合します。列挙型もポインタ型を持つことはできません。

列挙型の規則はC ++では異なります。

#include <iostream>
c j; //not allowed, unknown type name c before enum c{f} p; line
enum c j; //not allowed, forward declaration of enum type not allowed and variable can have an incomplete type but not when it's still a forward declaration in C++ unlike C
enum c{f, m} p;
typedef int d;
typedef int c; // not allowed in C++ as it clashes with enum c, but if just int c were used then the below usages of c j; would have to be enum c j;
[enum] c j;
enum m {n} ;
int main() {
  [enum] c j;
  enum d{l}; //not allowed in same scope as typedef but allowed here 
  d q;
  m y; //simple type specifier not allowed, need elaborated type specifier enum m to refer to enum m here
  p v; // not allowed, need enum p to refer to enum p
  std::cout << j;
}

C ++の列挙型変数は、単に符号なし整数などではなくなりました。また、列挙型であり、定数は列挙内でのみ割り当てることができます。ただし、これは捨てることができます。

#include <stdio.h>
enum a {l} c;
enum d {f} ;
int main() {
  c=0; // not allowed;
  c=l;
  c=(a)1;
  c=(enum a)4;
  printf("%llu", c); //4
}

列挙型クラス

enum struct と同じです enum class

#include <stdio.h>
enum class a {b} c;
int main() {
  printf("%llu", a::b<1) ; //not allowed
  printf("%llu", (int)a::b<1) ;
  printf("%llu", a::b<(a)1) ;
  printf("%llu", a::b<(enum a)1);
  printf("%llu", a::b<(enum class a)1) ; //not allowed 
  printf("%llu", b<(enum a)1); //not allowed
}

スコープ解決演算子は、スコープのない列挙型でも引き続き使用できます。

#include <stdio.h>
enum a: bool {l, w} ;
int main() {
  enum a: bool {w, l} f;
  printf("%llu", ::a::w);
}

wはスコープで何か他のもののように定義することはできませんので、しかし、差がない::wとは::a::w

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.