2

This question arises from an old question:

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

Answers below that question are just conflicting with each other. For example, one answer said "You need to use typedef", another argued "You don't need a typedef", a comment under which wrote "You cannot use enum strategy { ... }; in C -- you can and should do it in C++ though." This really confuses me. What's more, that OP's code compiles without even warnings using clang on all my computers.

So, my question is: In C, what's the best practice to use enum variables?

Community
  • 1
  • 1
nalzok
  • 14,965
  • 21
  • 72
  • 139
  • Doesn't [Johannes Schaub's answer](http://stackoverflow.com/a/1104903/1392132) explain this? – 5gon12eder Feb 09 '16 at 13:52
  • @5gon12eder Yes, that's what the "another" refers to. But a comment below it says we cannot write this in C, as mentioned in my question. – nalzok Feb 09 '16 at 13:55
  • 1
    That comment is just wrong. – 5gon12eder Feb 09 '16 at 13:58
  • To quote from Wikipedia: "The original K&R dialect of the C programming language did not have enumerated types, but they were added in the ANSI standard for C, which became C89." – Amnon Feb 09 '16 at 13:58
  • @Amnon You mean `C89 = ANSI C + enum type` ? – nalzok Feb 09 '16 at 13:59
  • @sun qingyao more like `C89 = K&R + enum type + other stuff I don't know about...` :-) – Amnon Feb 09 '16 at 14:04
  • C89 adds *far* more to K&R than just `enum` types. Function prototypes and ANSI-style function definitions are probably the most famous ones. These days, I suspect that some C programmers don't even realize there is an alternative style of function definition. – John Bollinger Feb 09 '16 at 14:15
  • Isn't K&R style removed in newer standards ?!! – nalzok Feb 09 '16 at 14:18
  • @sunqingyao, nope. K&R-style function definitions are still in the latest C standard, C2011. I'm inclined to suppose that they are retained mostly for the sake of legacy code. – John Bollinger Feb 09 '16 at 14:30

3 Answers3

2

C has two namespaces, one for variables and functions and another for things like enums and structs. Let's call the second one the enum namespace.

When you declare an enum like enum Foo {Bar1, Bar2};, Foo, and the enumeration constants will be put into the enum namespace.

This means that you have to use the enum prefix whenever you want to use that enumeration. Which is a lot of inconvenient typing! (Out of interest, the same applies to struct).

Fortunately C allows you to borrow things from the enum namespace, so they appear in the variable and function namespace too. You do that using a typedef:

typedef enum Foo {Bar1, Bar2} Foo;

The first Foo is its name in the enum namespace, the second Foo is its name in the function and variable namespace.

You don't need to do this in C++: unless you use namespace explicitly, everything is in the global namespace.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
2

Despite its large number of votes and its highly upvoted answers, the question you reference is based on a completely false premise. It asserts that this code is erroneous in C:

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

In fact, however, that code is perfectly fine in C89, C99, and C2011. It declares variable strategy to be of an anonymous enumerated type with the given enumeration constants, and it initializes that variable to one of the three values defined for its type. The error messages the OP of that question reported may have been emitted by a non-conforming compiler, or perhaps there was more to the story than that OP presented.

On the other hand, that form of variable declaration is of comparatively little practical use, because no other object can be declared to have the same type as the variable declared there. There are two alternatives for declaring an enumerated type so that it can be used in multiple declarations:

  1. Declare the type with a tag: enum strategy_tag {RANDOM, IMMEDIATE, SEARCH} /* ... */ ;
  2. Use a typedef to name the type: typedef enum {RANDOM, IMMEDIATE, SEARCH} strategy_type;

How you then refer to the type depends on which form you used. If you declare the type as a tagged enum, then you can use it in declarations by referencing the tag:

enum strategy_tag my_strategy = RANDOM;

. If you declare the type as a typedef'd name then you use the defined name as the type name

strategy_type my_strategy = IMMEDIATE;

. These are not exclusive; you can both tag an enum and declare a typedef for it, in separate statements ...

enum strategy_tag {RANDOM, IMMEDIATE, SEARCH};
typedef enum strategy_tag strategy_type;

... or even in one ...

typedef enum strategy_tag {RANDOM, IMMEDIATE, SEARCH} strategy_type;

. If you use both forms of type declaration then you can use either or both forms of type specifier in variable declarations.

Additionally, be aware that the namespaces for enum tags, type names, and variable names are all separate, so although it might be a bit confusing, you can do this:

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

As for which way is best, the answer is not so clear. If you have an established set of coding conventions that address this question then of course it is best to follow the conventions. If not then you can choose, but be consistent, at least within any given project. I tend to think that typedefs are overused in general, but using then to name enums is one of the uses I think makes sense. If you do that, however, then I suggest not declaring tags for typedef'd enums -- since you're after consistency, explicitly providing a mechanism for inconsistency is counterproductive.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
1

This is a bit subjective, but there is an industry de facto standard in C and C++ both. It goes:

If you need to declare instances (variables) of the type, then you should keep type definitions completely separate from the variable declarations.

When you mix them up, the code gets confusing to read. This goes for enums and all other types. Example:

typedef enum
{
  THIS,
  THAT
} mytype_t;

...

mytype_t variable;

However, as a special case: if you don't intend to declare any variables of a certain type, but just want a more readable way than #define to enumerate several integer constants belonging to the same group, then it is fine to do like this:

enum
{
  THIS_CONSTANT = 123,
  THAT_CONSTANT = 567
};

...

int something = THIS_CONSTANT + THAT_CONSTANT;
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Using a `_t` as your own type, and from one of the most respected contributors on this site. Naughty step for you. – Bathsheba Feb 09 '16 at 14:04
  • 2
    @Bathsheba Why? It is also de facto industry standard naming convention and there is nothing in the C standard disallowing it, quite the contrary. See 7.1.3 regarding what's not allowed. An underscore at the beginning of an identifier is what you should watch out for. – Lundin Feb 09 '16 at 14:05
  • 2
    @Bathsheba Aah I understand where you got that idea from. From POSIX. I really couldn't care less, all I care about is the C standard. If POSIX chose to deviate from the C standard or add non-standard restrictions, then that's their problem, not mine. I never work with POSIX systems, personally. – Lundin Feb 09 '16 at 14:14