如何在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 2014年

2
@Solver为什么此语法错误?
MCG 2015年

6
@MCQ,为坏死坏死:在C语言中问题的语法没有错。它声明strategy为具有匿名枚举类型,并为其分配该类型的声明值之一。而且,如果我将代码包装在其他琐碎的main()函数中,那么使用gcc 4.4.7,即使没有警告,它也可以很好地编译。尽管答案不多,但有些答案暗示的是相同的。
John Bollinger

5
大多数答案都缺少以下事实:问题中的两行代码不只是一个片段。它们是整个源文件。如果这两行包含在函数的主体中,则没有错误。如果它们出现在任何函数声明之外的文件范围内,您将得到OP询问的错误(尝试时还会出现其他错误)。根本问题是编译器试图将其strategy = IMMEDIATE;视为声明。它的格式在ANSI C之前的版本中是合法的,但在现代C中则是非法的。不允许在文件范围内进行分配。
基思·汤普森

3
@Solver:enum strategy { ... };定义一个名为的枚举类型enum strategy,其中strategy标记。enum { ... } strategy;定义一个匿名的枚举类型(无标记)一个名为的类型的单个对象strategy。两者都是完全合法的。他们只是意味着不同的事情。
基思·汤普森

Answers:


377

声明枚举变量是这样完成的:

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

我不能只是键入enum MyEnum {} myVar;然后使用变量myVar,如下所示:myVar = SOMEENUMCONSTANT;
Mushy

451

这是值得指出的是,你并不需要一个typedef。你可以像下面这样

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

您是否喜欢这是一个风格问题typedef。没有它,如果要引用枚举类型,则需要使用enum strategy。有了它,您可以说strategy

两种方式各有利弊。一个比较罗word,但将类型标识符保留在标记名称空间中,在这里它们不会与普通标识符冲突(请考虑struct statstat函数:它们也不会冲突),并且您会立即看到它是一种类型。另一个较短,但是将类型标识符带入普通名称空间。


6
它不应该被接受,因为它是错误的。您不能使用枚举策略{...}; 在C中-您可以并且应该在C ++中做到。
2014年

19
@Clearer:此代码完美地工作。这是一个工作示例:ideone.com/T0YV17 注意,它enum在两行上都使用了关键字。
RichieHindle 2014年

或“ typedef枚举策略{RANDOM,IMMEDIATE,SEARCH} strategy_t;” 使用枚举的开发人员可以使用他们想要的任何约定。
安迪·纽金特

效果很好:enum strategy { RANDOM, IMMEDIATE, SEARCH }; 然后,当您需要该枚举的实例时:`enum strategy myEnum;
user3629249

2
@AndyNugent不要这样做!* _t类型由POSIX保留
osvein

58

您试图声明strategy两次,这就是为什么您遇到上述错误的原因。以下作品没有任何投诉(与一起编译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;
}

如果不是上述内容,则将第二行更改为:

...
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了一个声明为strategydefault type 的变量的声明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未命名的枚举不是很有用,因为您不能具有该类型的任何其他变量,函数参数或返回值。如果您只需要一个变量,那就很好。
鲍勃·斯坦

3
有人不使用匿名枚举不会证明他们没有用。您不需要typedef。某些代码准则(kernel.org/doc/Documentation/CodingStyle)甚至不建议这样做。
martinkunev

2
这个答案也具有误导性。Tarc的答案是这里唯一正确的答案。
nightpool'2

13

如所写,您的代码没有错。您确定没有做过类似的事情吗

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

错误消息指向哪几行?当它说“先前的'战略'声明在这里”时,“这里”是什么,它表示什么?


6
他可能strategy = IMMEDIATE;在文件范围内做了。不能在所有功能之外的文件范围内进行分配。因此,编译器尝试尽最大努力解决错误,并假设他的意思是int strategy = IMMEDIATE;,此时发生了冲突。
Johannes Schaub-litb

2
这是最好的答案,在其他答案中有如此令人困惑的困惑。
2015年

12

@ThoAppelsin在对所发布问题的评论中是正确的。问题中张贴的代码段有效且没有错误。您遇到的错误一定是因为c源文件的任何其他地方存在其他语法错误。enum{a,b,c};定义了三个符号常量(abc),这与值的整数012分别,但是当我们使用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;

并且将具有相同的效果(即,aa作为一个int0值)。

您也可以这样做:

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

并且aaint与价值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 ++中,您可以使用“枚举”来定义新类型,而无需使用typedef语句。

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

我发现这种方法更加友好。

[编辑-澄清C ++的状态-我本来有这个,然后将其删除!]


是的,永远不要在C ++中将typedef与枚举(或结构,联合等)一起使用。

17
这个问题是针对C的,而不是针对C ++的。在C语言中,以上代码无效-您要么必须使用typedef,要么还要enum在变量声明中指定:枚举策略{RANDOM,IMMEDIATE,SEARCH}; ...列举策略myStrategy =立即;
帕维尔·米纳夫

@pavel-我的坏。我本来是用“ C ++”编写的,然后做了一些似乎与此矛盾的研究。
罗迪

@Pavel我认为这应该是描述使用的好处的单独答案enum Strategy。我做到了,见下文。
Johannes Schaub-litb

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} strategytypedef enum strategy {RANDOM, IMMEDIATE, SEARCH} strategy_type。相对于此有什么优势typedef enum {RANDOM, IMMEDIATE, SEARCH} strategy吗?为了完整性,您是否考虑将这些添加到您的答案中?
tjalling 2016年

是。我修改了答案。据我了解,一般情况下没有任何主要优势。
混淆

2
太好了,谢谢。可惜的是,答案之所以如此遥不可及,至少不是因为它以适当的解释明确地解决了原始问题。
tjalling

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

Tarc的答案是最好的。

枚举的大部分讨论都是红色鲱鱼。

比较此代码段:-

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需要在声明或函数内部设置变量。不能在全局范围内编写任意软件-特别是赋值。

他使用枚举{RANDOM,IMMEDIATE,SEARCH}而不是int的事实仅与它混淆了看不见的人的程度有关。问题中的重新定义错误消息表明这是作者做错的。

因此,现在您应该能够看到以下示例中第一个错误的原因,而其他三个错误的原因。

示例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枚举状态 {a = 0,b = 1,c = 2} 状态 ;

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);

new在C系列中,标识符是错误的选择,因为它是C ++中的运算符。
jww

0

C

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

在作用域中s用作具有完整类型的带符号整数的暂定定义的声明和q在作用域中用作具有不完整类型的带符号整数的暂定定义的声明(解析为作用域中的完整类型,因为类型定义存在于作用域中的任何位置范围)(与任何暂定定义一样,标识符qs可以使用相同类型的不完整或完整版本intenum stuff多次声明,但只能在范围中定义一次,即int q = 3;并且只能在子范围内重新定义),并且仅在定义后可用)。同样,您只能enum stuff在范围内使用一次完整类型,因为它充当类型定义。

enum stuff还在文件作用域(在之前和之下使用)和前向类型声明(该类型enum stuff可以有多个声明,但作用域中只有一个定义/完成)中提供了针对其的编译器枚举类型定义。。它也充当编译器指令,以在当前范围内a用rvalue 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)作为前缀,即after enum a {a} benum a c必须使用而不是a c。因为标签名称空间与标识符名称空间是分开的,enum a {a} b所以允许,但enum a {a, b} b不是因为常量与变量标识符(标识符名称空间)在同一名称空间中。typedef enum a {a,b} b也不允许,因为typedef名称是标识符名称空间的一部分。

C的类型enum bool和常量遵循以下模式:

+--------------+-----+-----+-----+
|   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

在这种情况下,常量和标识符都具有相同的类型bool,如果数字不能用该类型表示,则会发生错误。也许= 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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.