25

For example, never define a macro like this:

#define DANGER 60 + 2

This can potentially be dangerous when we do an operation like this:

int wrong_value = DANGER * 2; // Expecting 124

Instead, define like this because you don't know how the user of the macro may use it:

#define HARMLESS (60 + 2)

The example is trivial, but that pretty much explains my question. Are there any set of guidelines or best practices that you would recommend when writing a macro?

Thanks for your time!

a3f
  • 8,517
  • 1
  • 41
  • 46
Srikanth
  • 11,780
  • 23
  • 72
  • 92

11 Answers11

31

When doing a macro that is to run its argument and behave like an expression, this is idiomatic:

 #define DOIT(x) do { x } while(0)

This form has the following advantages:

  1. It needs a terminating semicolon
  2. It works with nesting and braces, e.g. with if/else
unwind
  • 391,730
  • 64
  • 469
  • 606
  • 1
    And what if `x` is only a single statement? For instance `foobar = 42`, do we still need to enclose it into `do{}while(0)`, or just putting it inside parentheses is enough? – Denilson Sá Maia Sep 03 '11 at 04:10
  • 1
    You would still need it if you want 1) the macro to **demand** a terminating semicolon, and 2) to use it as a stand-alone instruction in a one-line if-statement. – luis.espinal Nov 05 '11 at 20:46
  • 1
    Check this link. It explains it very well : http://kernelnewbies.org/FAQ/DoWhile0 – luis.espinal Nov 05 '11 at 20:47
31

Not only should you put parens around the arguments, you should put parens around the expression returned.

#define MIN(a,b)  a < b ? a : b     // WRONG  

int i = MIN(1,2); // works
int i = MIN(1,1+1); // breaks

#define MIN(a,b)  (a) < (b) ? (a) : (b)   // STILL WRONG

int i = MIN(1,2); // works
int i = MIN(1,1+1); // now works
int i = MIN(1,2) + 1; // breaks

#define MIN(a,b)  ((a) < (b) ? (a) : (b))   // GOOD

int i = MIN(1,2); // works
int i = MIN(1,1+1); // now works
int i = MIN(1,2) + 1; // works

However, MIN(3,i++) is still broken...

The best rule is only to use #defines only when NO OTHER APPROACH WILL WORK! I know you're asking about C instead of C++, but still bear his in mind.

Roddy
  • 66,617
  • 42
  • 165
  • 277
  • 1
    Maybe you should add a note about using enum instead of defining numeric constants with #define. – Jonathan Leffler Nov 26 '08 at 17:53
  • Ack! MIN(3, i++)! I never considered that case... That's gotta suck to debug that! – Adam Davis Nov 26 '08 at 18:42
  • Of course, the fix is trivial - make it a function, or take the increment out of that line of code. I don't agree that macros shouldn't be used except when no other approach will work - Like any other tool macros can be dangerous if improperly used, but done well they can decrease development time – Adam Davis Nov 26 '08 at 18:45
10

Use parenthesis around the entire macro and around each argument referred to in the expansion list:

#define MAX(x, y) ((x) > (y) ? (x) : (y))

Avoid writing macros that evaluate their arguments multiple times. Such macros will not behave as expected when arguments have side effects:

MAX(a++, b);

Will evaluate a++ twice if a is greater than b.


Use UPPERCASE names for macros to make it clear it is a macro and not a function so that the differences can be considered accordingly (another general good practice is not passing arguments that have side effects to functions either).


Don't use macros to rename types like this:

#define pint int *

because it won't behave as expected when someone types

pint a, b;

Use typedefs instead.

Robert Gamble
  • 106,424
  • 25
  • 145
  • 137
7

use static const values instead of macros for constant values, integral or other. The compiler can often optimize them away, and they remain a 1-st class citizen in the language's type system.

static const int DANGER = 60 + 2;
John Dibling
  • 99,718
  • 31
  • 186
  • 324
5

In the expansion, put parenthesis around the arguments, so that if they pass in a expression you will get the intended behavior.

#define LESS_THAN(X,Y) (((X) < (Y) ? (X) : (Y))
EvilTeach
  • 28,120
  • 21
  • 85
  • 141
  • Ironically, this is a bad macro because it evaluates its arguments twice. –  Nov 19 '21 at 17:44
5

Response to the MAX/MIN macros, taken from GCC hacks in the Linux kernel:

#define min(x, y) ({                       \
        typeof(x) _min1 = (x);             \
        typeof(y) _min2 = (y);             \
        (void) (&_min1 == &_min2);         \
        _min1 < _min2 ? _min1 : _min2; })
dalle
  • 18,057
  • 5
  • 57
  • 81
2

Use fairly unique names for your macros, since they have global scope and can clash with anything, so:

#define MAX 10

could easily clash with other code, so:

#define MYPROJECT_MAX 10

or something even more unique, would be better.

I've seen cases where a clash of this kind didn't produce a compile error, but generated slightly wrong code, so it can be quite insidious.

DarthPingu
  • 241
  • 1
  • 4
2

Undefine your macros.

Your #defines should matched with an #undef. This prevents the preprocessor from getting clogged up and affecting unintended pieces of code.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
lillq
  • 14,758
  • 20
  • 53
  • 58
2

For multiple line macros, use a do { } while (0):

#define foo(x) do {  \
    (x)++;           \
    printf("%d", x); \
} while(0)

Had you done

#define foo(x) {     \
    (x)++;           \
    printf("%d", x); \
}

instead,

if (xyz)
    foo(y);
else
    foo(z);

would've failed.

Also, be careful when introducing temporary variables in macros:

#define foo(t) do {    \
    int x = (t);       \
    printf("%d\n", x); \
} while(0)

int x = 42;
foo(x);

will print 0 and not 42.

If you have a complicated expression which needs to return a value, you can use the comma operator:

#define allocate(foo, len) (foo->tmp = foo->head, foo->head += len, foo->tmp)
sanjoyd
  • 3,260
  • 2
  • 16
  • 22
1

If you're careful and expert, you may be able to accomplish DRY (Don't-Repeat-Yourself) code, by using macros as simple code generators. You do have to explain to other programmers what you're doing, but it can save a lot of code. For example, the list-macro technique:

// define a list of variables, error messages, opcodes
// or anything that you have to write multiple things about
#define VARLIST \
    DEFVAR(int, A, 1) \
    DEFVAR(double, B, 2) \
    DEFVAR(int, C, 3) \

// declare the variables
#define DEFVAR(typ, name, val) typ name = (val);
    VARLIST
#undef  DEFVAR

// write a routine to set a variable by name
void SetVar(string varname, double value){
    if (0);
    #define DEFVAR(typ, name, val) else if (varname == #name) name = value;
        VARLIST
    #undef  DEFVAR
    else printf("unrecognized variable %s\n", varname);
}

// write a routine to get a variable's value, given its name
// .. you do it ..

Now, if you want to add a new variable, delete one, or rename one, it's a 1-line edit.

Mike Dunlavey
  • 40,059
  • 14
  • 91
  • 135
0

See how I hate this:

void bar(void) {
    if(some_cond) {
        #define BAZ ...
        /* some code */
        #undef BAZ
    }
}

Always put them like this:

void bar(void) {
    if(some_cond) {
#define BAZ ...
        /* some code */
#undef BAZ
    }
}
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212