如何在C语言中定义枚举类型(enum)?

287

我不确定如何正确使用C语言中的枚举。我有以下代码:

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

但是这段代码无法编译,会出现以下错误:

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

我做错了什么?


7
多年来一直存在的问题,可能没有人会看到这个问题;但为什么这会出现错误?据我所知,按照问题中的方式应该完美地工作。 - Utkan Gezer
2
@Solver,为什么这个语法是错误的? - MCG
6
@MCQ,挖掘已经被挖掘的尸体:问题中呈现的语法在C语言中不是错误的。它声明strategy具有匿名枚举类型,并将该类型声明的值之一分配给它。此外,如果我将呈现的代码包装在一个其他方面微不足道的main()函数中,则使用gcc 4.4.7编译对我来说很好,甚至没有警告。一些答案意味着相同的事情,尽管没有那么明确地表达。 - John Bollinger
7
大多数答案都没有涉及到这个事实:问题中的两行代码不仅仅是一小段代码片段。它们是整个源文件。如果将这两行代码包含在函数体内,就不会出错。如果它们出现在文件作用域中,在任何函数声明之外,您将得到OP所问的错误(当我尝试时还有其他一些错误)。根本问题在于编译器试图将strategy = IMMEDIATE;视为一个声明。它具有在ANSI C之前合法的形式,但在现代C中是非法的。在文件作用域中不允许赋值。 - Keith Thompson
3
@Solver: enum strategy { ... }; 定义了一个名为 enum strategy 的枚举类型,其中 strategy 是标签。enum { ... } strategy; 定义了一个匿名的枚举类型(没有标签)一个名为 strategy 的该类型的单一对象。这两种定义方式都是合法的,它们只是表示不同的含义。 - Keith Thompson
显示剩余4条评论
13个回答

456

值得指出的是,您不必须使用 typedef。您可以按照以下方式执行

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

关于你是否喜欢使用typedef,这是一个风格问题。如果不使用它,当你想引用枚举类型时,需要使用enum strategy。而通过使用typedef,你可以直接使用strategy来引用。

两种方法各有优缺点。第一种方法更冗长,但将类型标识符保留在标签命名空间中,避免了与普通标识符的冲突(想想struct statstat函数:它们也不会冲突),并且可以立即看出它是一个类型。第二种方法较短,但会将类型标识符带入普通命名空间。


7
这个答案不能被接受,因为它是错误的。在C语言中,你不能使用“enum strategy {...}”这种方式,但在C++中可以且应该使用它。 - Clearer
19
这段代码运行完美。这里有一个可工作的示例:https://ideone.com/T0YV17 请注意,它在两行上都使用了 enum 关键字。 - RichieHindle
这段程序相关的内容翻译成中文如下:这个代码非常好用:enum strategy { RANDOM, IMMEDIATE, SEARCH }; 当你需要一个该枚举类型的实例时,可以使用以下代码:enum strategy myEnum; - user3629249
2
@AndyNugent 不要那样做!*_t 类型被 POSIX 保留。 - osvein
为什么在C99中 enum MyEnum {} useEnum; 不能正常工作?为什么需要使用 enum MyEnum variableName?在使用Visual Studio 2015的C语言中,前面提到的代码可以正常工作,而不需要额外的输入。 - Mushy
显示剩余2条评论

397

声明枚举变量的方法如下:

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

不过,你可以使用typedef来缩短变量声明,例如:

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

有一种命名规范可以区分类型和变量,这是一个好主意:

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

2
但是 OP 想要一个匿名枚举类型的变量。 - osvein
我能不能只输入 enum MyEnum {} myVar; 然后像下面这样使用变量 myVar: myVar = SOMEENUMCONSTANT; - Mushy

60
你试图声明strategy两次,这就是为令你收到上述错误的原因。以下代码可以正常工作(使用gcc -ansi -pedantic -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视为一个名为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;
}

53
当你说
enum {RANDOM, IMMEDIATE, SEARCH} strategy;

你需要创建一个名为“strategy”的匿名枚举的单个实例变量。这样做并没有什么太大的用处 - 你需要使用typedef:

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

10
为什么这样做没有用呢?如果我不关心类型的名称,为什么还要给它一个名称?这里唯一的目的是给变量命名,这样才能给它分配新值。 - MSalters
3
我说它不是非常有用,我也不认为它是。当然,我不会在自己的代码中使用这个模式。你的情况可能会有所不同。 - anon
1
但是为什么这样做并不是很有用呢? - Horse SMith
4
未命名的枚举并不是很有用,因为你无法拥有该类型的任何其他变量、函数参数或返回值。如果只需要一个变量,那么这样就可以了。@HorseSMith - Bob Stein
3
不使用匿名枚举并不意味着它们没有用处。您不需要typedef关键字。甚至有些代码指南(kernel.org/doc/Documentation/CodingStyle)反对使用它。 - martinkunev
2
这个回答同样是误导性的。Tarc的回答才是正确的。 - nightpool

12

按照您的代码编写,没有任何问题。 您确定您没有做过类似于以下操作

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

这些错误消息指向哪些行?当出现“strategy的先前声明在这里”的消息时,“这里”是指什么,它显示了什么?


7
他可能在文件作用域下执行了 strategy = IMMEDIATE;。在所有函数之外,不能在文件作用域下进行赋值操作。因此编译器尝试从错误中找到最佳解决方案,并假设他的意思是 int strategy = IMMEDIATE;,于是就出现了冲突。 - Johannes Schaub - litb
2
这是最好的答案,其他答案中有太多混淆了,真是让人痛苦。 - unwind

11

值得一提的是,在C ++中,您可以使用"enum"来定义新类型,而无需typedef语句。

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

我认为这种方法更加友好。

[编辑 - 澄清了 C++ 的状态 - 我最初放在里面,然后将其删除!]


是的,在C++中,您不应该将typedef与枚举(或结构体、联合等)一起使用。 - anon
17
这个问题是关于 C 语言,而不是 C++。在 C 语言中,上述代码是无效的——你必须使用 typedef,或者在变量声明中同时指定 enumtypedef enum {RANDOM, IMMEDIATE, SEARCH} Strategy; ... Strategy myStrategy = IMMEDIATE; - Pavel Minaev
@pavel - 我的错。我最初写上了 "in C++",然后进行了一些研究,发现似乎与此相矛盾。 - Roddy
@Pavel 我认为应该单独回答并描述使用“enum Strategy”的好处。我已经这样做了,请看下面的回答。 - Johannes Schaub - litb

11

@ThoAppelsin在他对问题的评论中是正确的。问题中发布的代码片段是有效的且没有错误。你遇到的错误可能是由于C源文件的其他位置存在不良语法所致。enum{a,b,c};定义了三个符号常量(a,bc),它们都是整数,分别具有值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 是一个值为 0int 类型)。

你也可以这样做:

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

同时aa将会是值为7int类型。

因为您无法使用enum重复定义符号常量,正如我之前所说,如果想要使用enum声明int变量,则必须使用标签:

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

9

关于声明似乎有些混淆。

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
是的,我修改了我的答案。据我所知,在一般情况下并没有什么主要优势。 - Confuse
2
太好了,你的回答完全覆盖了所有问题,谢谢。很遗憾它在答案列表中排名如此之低,尤其是因为它明确地回答了原始问题,并给出了适当的解释。 - tjalling

2

我的最喜欢且唯一使用的构建方式始终是:

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

我相信这将解决你所遇到的问题。从我的角度来看,使用新类型是正确的选择。


2

如果声明枚举的名称,则不会出现错误。

如果没有声明,则必须使用 typedef

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

它不会显示错误...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接