Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Choosing between enum or define in C?

Tags:

c

enums

casting

I noticed that most of the predefined values in libc are written using #define directives. For example, the whence parameter takes an int in fseek where (from my knowledge) an enum would have been better. There are plenty of examples like this that should obviously exist for a reason (other than back-compatibility issues).

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

As a practical example consider a typedef representing an input/output channel, as it could be the case on the kernel. The gpio can be configured either in input or output. Is it worth to use an enum directive here?

typedef struct gpio {
    size_t port; 
    size_t bit;
    enum { DIR_INPUT, DIR_OUTPUT } direction; // or `bool`?
    bool value;
} gpio;

Notice that the enum can be implemented in three different manners:

i) Type:

typedef enum gpio_direction {   
   DIR_OUTPUT
   DIR_INPUT
} gpio_direction;

ii) Global enum

enum gpio_direction {   
   DIR_OUTPUT
   DIR_INPUT
} gpio_direction;

iii) Anonymous enum (as showed in my example).

like image 759
nowox Avatar asked May 17 '16 07:05

nowox


People also ask

Which is better #define or enum?

The use of an enumeration constant (enum) has many advantages over using the traditional symbolic constant style of #define. These advantages include a lower maintenance requirement, improved program readability, and better debugging capability.

When to use enum VS define?

One of the differences between the two is that #define is a pre-processor directive while enum is part of the actual C language. #define statements are processed by the compiler before the first line of C code is even looked at!

When should we use enum in C?

Enumeration (or enum) is a user defined data type in C. It is mainly used to assign names to integral constants, the names make a program easy to read and maintain. Hereby mistake, the state of wed is 2, it should be 3.

Why we need enums advantages over macros?

In terms of readability, enumerations make better constants than macros, because related values are grouped together. In addition, enum defines a new type, so the readers of your program would have easier time figuring out what can be passed to the corresponding parameter.


2 Answers

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

like image 94
Hans Lub Avatar answered Oct 24 '22 03:10

Hans Lub


While one probably should use other solutions where such are available there are still situations where macros are required. Some are historical reasons, but there are also reasons valid still today.

For example you mention the whence argument to fseek (ie SEEK_SET, SEEK_CUR and `SEEK_END). These are (for historical reasons) specified to be macros in the standard - so the implementor has to define them as macros to be fully compliant, some evil programmer could write (and blame the library for consequences):

#include <stdio.h>

#ifndef SEEK_CUR
int evil = *(int*)0;
#endif

But as far as I can see theres no reason why they couldn't write:

enum __WHENCE_T {
    __SEEK_CUR,
    __SEEK_END,
    __SEEK_SET
};

#define SEEK_CUR __SEEK_CUR
#define SEEK_END __SEEK_END
#define SEEK_SET __SEEK_SET

Another historical reason is that the compiler might have produced more efficient code when using macros. Code that were written back then might still be around and containing constructs that worked better back then (and still works pretty god today).

Modern reasons are if you need to use the values outside of the C-compiler. For example as mentioned if you want to use the value in assembler code (or some other language). All you need to do is to make sure that the header doesn't expand to something (that contain C code) unless it's compiled with a C compiler. For example:

#define NUMBER 42

#ifdef __STDC__
extern int variable;

int function(void);
#endif

if included from assembler the macro NUMBER would still be expandable (and expand to the same thing as for C programs).

like image 37
skyking Avatar answered Oct 24 '22 04:10

skyking