Difference Between Enum and Define Statements

Difference between Enum and Define Statements

enum defines a syntactical element.

#define is a pre-preprocessor directive, executed before the compiler sees the code, and therefore is not a language element of C itself.

Generally enums are preferred as they are type-safe and more easily discoverable. Defines are harder to locate and can have complex behavior, for example one piece of code can redefine a #define made by another. This can be hard to track down.

#define or enum?

Since the states are related elements I think is better to have an enum defining them.

Choosing between enum or define in C?

There are plenty of examples like this that should obviously exist for
a reason

One reason is that there is no portable way to refer to a C enum from assembly code. Low-level code usually was (and often still is) written in assembly, so constants used by that low-level code will be specified by a #define rather than with enum.

Another reason is the idiom if (verbosity & WAKE_THE_NEIGHBOURS) where a #defined value is used to specify a bit position.

So I am wondering in which case is it better to use #define whereas
enum is a type-safe alternative more easily discoverable

In all other cases I would (today - using #define is also somewhat of a tradition) use an enum, so that if (verbosity == RED) will provoke a warning (if you use e.g. gcc with -Wall).

Why use enum when #define is just as efficient?

The advantages of enum show up when you have a long list of things you want to map into numbers, and you want to be able to insert something in the middle of that list. For example, you have:


pears 0
apples 1
oranges 2
grapes 3
peaches 4
apricots 5

Now you want to put tangerines after oranges. With #defines, you'd have to redefine the numbers of grapes, peaches, and apricots. Using enum, it would happen automatically. Yes, this is a contrived example, but hopefully it gives you the idea.

static const vs #define vs enum

It depends on what you need the value for. You (and everyone else so far) omitted the third alternative:

  1. static const int var = 5;
  2. #define var 5
  3. enum { var = 5 };

Ignoring issues about the choice of name, then:

  • If you need to pass a pointer around, you must use (1).
  • Since (2) is apparently an option, you don't need to pass pointers around.
  • Both (1) and (3) have a symbol in the debugger's symbol table - that makes debugging easier. It is more likely that (2) will not have a symbol, leaving you wondering what it is.
  • (1) cannot be used as a dimension for arrays at global scope; both (2) and (3) can.
  • (1) cannot be used as a dimension for static arrays at function scope; both (2) and (3) can.
  • Under C99, all of these can be used for local arrays. Technically, using (1) would imply the use of a VLA (variable-length array), though the dimension referenced by 'var' would of course be fixed at size 5.
  • (1) cannot be used in places like switch statements; both (2) and (3) can.
  • (1) cannot be used to initialize static variables; both (2) and (3) can.
  • (2) can change code that you didn't want changed because it is used by the preprocessor; both (1) and (3) will not have unexpected side-effects like that.
  • You can detect whether (2) has been set in the preprocessor; neither (1) nor (3) allows that.

So, in most contexts, prefer the 'enum' over the alternatives. Otherwise, the first and last bullet points are likely to be the controlling factors — and you have to think harder if you need to satisfy both at once.

If you were asking about C++, then you'd use option (1) — the static const — every time.

Difference between “static const”, “#define”, and “enum” in performance and memory usage aspects

The compiler would treat them the same given basic optimization.

It's fairly easy to check - consider the following c code :

#define a 1
static const int b = 2;
typedef enum {FOUR = 4} enum_t;

int main() {

enum_t c = FOUR;

printf("%d\n",a);
printf("%d\n",b);
printf("%d\n",c);

return 0;
}

compiled with gcc -O3:

0000000000400410 <main>:
400410: 48 83 ec 08 sub $0x8,%rsp
400414: be 01 00 00 00 mov $0x1,%esi
400419: bf 2c 06 40 00 mov $0x40062c,%edi
40041e: 31 c0 xor %eax,%eax
400420: e8 cb ff ff ff callq 4003f0 <printf@plt>
400425: be 02 00 00 00 mov $0x2,%esi
40042a: bf 2c 06 40 00 mov $0x40062c,%edi
40042f: 31 c0 xor %eax,%eax
400431: e8 ba ff ff ff callq 4003f0 <printf@plt>
400436: be 04 00 00 00 mov $0x4,%esi
40043b: bf 2c 06 40 00 mov $0x40062c,%edi
400440: 31 c0 xor %eax,%eax
400442: e8 a9 ff ff ff callq 4003f0 <printf@plt>

Absolutely identical assembly code, hence - the exact same performance and memory usage.

Edit: As Damon stated in the comments, there may be some corner cases such as complicated non literals, but that goes a bit beyond the question.

C++ - enum vs. const vs. #define

Consider this code,

#define WIDTH 300

enum econst
{
eWidth=300
};

const int Width=300;

struct sample{};

int main()
{
sample s;
int x = eWidth * s; //error 1
int y = WIDTH * s; //error 2
int z = Width * s; //error 3
return 0;
}

Obviously each multiplication results in compilation-error, but see how the GCC generates the messages for each multiplication error:

prog.cpp:19: error: no match for
‘operator*’ in ‘eWidth * s’

prog.cpp:20: error: no match for
‘operator*’ in ‘300 * s’

prog.cpp:21: error: no match for
‘operator*’ in ‘Width * s’

In the error message, you don't see the macro WIDTH which you've #defined, right? That is because by the time GCC makes any attempt to compile the line corresponds to second error, it doesn't see WIDTH, all it sees only 300, as before GCC compiles the line, preprocessor has already replaced WIDTH with 300. On the other hand, there is no any such thing happens with enum eWidth and const Width.

See the error yourself here : http://www.ideone.com/naZ3P


Also, read Item 2 : Prefer consts, enums, and inlines to #defines from Effective C++ by Scott Meyers.

What's the difference between these two enum definitions?

They are essentially the same thing. The difference is that the first one is more "C++-styled" and the second one is more "C-Styled".

C++ makes some semantic differences, but you can write C-Style enums (that are present throughout the Cocoa frameworks) for backwards compatibility.



Related Topics



Leave a reply



Submit