[c] How to define an enumerated type (enum) in C?

I'm not sure what is the proper syntax for using C enums. I have the following code:

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

But this does not compile, with the following error:

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

What am I doing wrong?

This question is related to c enums

The answer is


If you declare the name for enumeration no error will occur.

If not declared, you have to use a typedef:

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

It will not display an error...


You're trying to declare strategy twice, and that's why you're getting the above error. The following works without any complaints (compiled with 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;
}

If instead of the above, the second line were changed to:

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

From the warnings, you could easily see your mistake:

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

So the compiler took strategy = IMMEDIATE for a declaration of a variable called strategy with default type int, but there was already a previous declaration of a variable with this name.

However, if you placed the assignment in the main() function, it would be a valid code:

#include <stdio.h>

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

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

    return 0;
}

I tried with gcc and come up with for my need I was forced to use the last alternative, to compile with out error.

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

It's worth mentioning that in C++ you can use "enum" to define a new type without needing a typedef statement.

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

I find this approach a lot more friendly.

[edit - clarified C++ status - I had this in originally, then removed it!]


My favorite and only used construction always was:

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

I believe that this will remove your problem you have. Using new type is from my point of view right option.


As written, there's nothing wrong with your code. Are you sure you haven't done something like

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

What lines do the error messages point to? When it says "previous declaration of 'strategy' was here", what's "here" and what does it show?


It's worth pointing out that you don't need a typedef. You can just do it like the following

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

It's a style question whether you prefer typedef. Without it, if you want to refer to the enumeration type, you need to use enum strategy. With it, you can just say strategy.

Both ways have their pro and cons. The one is more wordy, but keeps type identifiers into the tag-namespace where they won't conflict with ordinary identifiers (think of struct stat and the stat function: these don't conflict either), and where you immediately see that it's a type. The other is shorter, but brings type identifiers into the ordinary namespace.


C

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

Declaration which acts as a tentative definition of a signed integer s with complete type and declaration which acts as a tentative definition of signed integer q with incomplete type in the scope (which resolves to the complete type in the scope because the type definition is present anywhere in the scope) (like any tentative definition, the identifiers q and s can be redeclared with the incomplete or complete version of the same type int or enum stuff multiple times but only defined once in the scope i.e. int q = 3; and can only be redefined in a subscope, and only usable after the definition). Also you can only use the complete type of enum stuff once in the scope because it acts as a type definition.

A compiler enumeration type definition for enum stuff is also made present at file scope (usable before and below) as well as a forward type declaration (the type enum stuff can have multiple declarations but only one definition/completion in the scope and can be redefined in a subscope). It also acts as a compiler directive to substitute a with rvalue 0, b with -4, c with 5, d with -2, e with -3, f with -1 and g with -2 in the current scope. The enumeration constants now apply after the definition until the next redefinition in a different enum which cannot be on the same scope level.

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 

The tag namespace shared by enum, struct and union is separate and must be prefixed by the type keyword (enum, struct or union) in C i.e. after enum a {a} b, enum a c must be used and not a c. Because the tag namespace is separate to the identifier namespace, enum a {a} b is allowed but enum a {a, b} b is not because the constants are in the same namespace as the variable identifiers, the identifier namespace. typedef enum a {a,b} b is also not allowed because typedef-names are part of the identifier namespace.

The type of enum bool and the constants follow the following pattern in 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 |
+-----------+-----+---------------+------+

This compiles fine in 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++

In C++, enums can have a type

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

In this situation, the constants and the identifier all have the same type, bool, and an error will occur if a number cannot be represented by that type. Maybe = 2, which isn't a bool. Also, True, False and Bool cannot be lower case otherwise they will clash with language keywords. An enum also cannot have a pointer type.

The rules for enums are different in 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;
}

Enums variables in C++ are no longer just unsigned integers etc, they're also of enum type and can only be assigned constants in the enum. This can however be cast away.

#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 classes

enum struct is identical to 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
}

The scope resolution operator can still be used for non-scoped enums.

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

But because w cannot be defined as something else in the scope, there is no difference between ::w and ::a::w


Tarc's answer is the best.

Much of the enum discussion is a red herring.

Compare this code snippet:-

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

which gives

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

with this one which compiles with no problem.

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

The variable strategy needs to be set at declaration or inside a function etc. You cannot write arbitrary software - assignments in particular - at the global scope.

The fact that he used enum {RANDOM, IMMEDIATE, SEARCH} instead of int is only relevant to the extent that it has confused people that can't see beyond it. The redefinition error messages in the question show that this is what the author has done wrong.

So now you should be able to see why the first of the example below is wrong and the other three are okay.

Example 1. WRONG!

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

Example 2. RIGHT.

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

Example 3. RIGHT.

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

Example 4. RIGHT.

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

If you have a working program you should just be able to paste these snippets into your program and see that some compile and some do not.


There seems to be a confusion about the declaration.

When strategycomes before {RANDOM, IMMEDIATE, SEARCH} as in the following,

enum strategy {RANDOM, IMMEDIATE, SEARCH};

you are creating a new type named enum strategy. However, when declaring the variable, you need to use enum strategy itself. You cannot just use strategy. So the following is invalid.

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

While, the following is valid

enum strategy {RANDOM, IMMEDIATE, SEARCH};

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

When strategy comes after {RANDOM, IMMEDIATE, SEARCH}, you are creating an anonymous enum and then declaring strategy to be a variable of that type.

So now, you can do something like

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

However, you cannot declare any other variable of type enum {RANDOM, IMMEDIATE, SEARCH} because you have never named it. So the following is invalid

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

You can combine both the definitions too

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

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

Typedef as noted before is used for creating a shorter variable declaration.

typedef enum {RANDOM, IMMEDIATE, SEARCH} strategy;

Now you have told compiler that enum {RANDOM, IMMEDIATE, SEARCH} is synonomous to strategy. So now you can freely use strategy as variable type. You don't need to type enum strategy anymore. The following is valid now

strategy x = RANDOM;

You can also combine Typedef along with enum name to get

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

There's not much advantage of using this method apart from the fact that you can now use strategy and enum strategyName interchangeably.

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

enum strategyName a = RANDOM;
strategy b = SEARCH;

When you say

enum {RANDOM, IMMEDIATE, SEARCH} strategy;

you create a single instance variable, called 'strategy' of a nameless enum. This is not a very useful thing to do - you need a typedef:

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

@ThoAppelsin in his comment to question posted is right. The code snippet posted in the question it is valid and with no errors. The error you have must be because other bad syntax in any other place of your c source file. enum{a,b,c}; defines three symbolic constants (a, b and c) which are integers with values 0,1 and 2 respectively, but when we use enum it is because we don't usually care about the specific integer value, we care more about the meaning of the symbolic constant name. This means you can have this:

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

and this will output 1.

This also will be valid:

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

and will output the same as before.

If you do this:

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

you will have an error, but if you do this:

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

you will not have any error.

you can do this:

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

and aa will be an integer variable with value 0. but you can also do this:

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

and will have the same effect (that is, aa being an int with 0 value).

you can also do this:

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

and aa will be int with value 7.

because you cannot repeat symbolic constant definition with the use of enum, as i have said previously, you must use tags if you want to declare int vars with the use of enum:

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

the use of typedef it is to safe you from writing each time enum tag1 to define variable. With typedef you can just type Tag1:

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

You can also have:

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

Last thing to say it is that since we are talking about defined symbolic constants it is better to use capitalized letters when using enum, that is for example:

enum {A,B,C};

instead of

enum {a,b,c};