Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Good Programming Practices for Macro Definitions (#define) in C [closed]

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!

like image 864
Srikanth Avatar asked Nov 26 '08 15:11

Srikanth


People also ask

How do you write a macro definition?

A macro is a fragment of code that is given a name. You can define a macro in C using the #define preprocessor directive. Here's an example. Here, when we use c in our program, it is replaced with 299792458 .

What are the advantages of using macro definitions in a program?

Speed versus size The main benefit of using macros is faster execution time. During preprocessing, a macro is expanded (replaced by its definition) inline each time it is used. A function definition occurs only once regardless of how many times it is called.

What are macros in programming?

A macro (which stands for "macroinstruction") is a programmable pattern which translates a certain sequence of input into a preset sequence of output. Macros can make tasks less repetitive by representing a complicated sequence of keystrokes, mouse movements, commands, or other types of input.


10 Answers

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.

like image 104
Roddy Avatar answered Oct 05 '22 06:10

Roddy


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
like image 30
unwind Avatar answered Oct 05 '22 06:10

unwind


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.

like image 44
Robert Gamble Avatar answered Oct 05 '22 07:10

Robert Gamble


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;
like image 33
John Dibling Avatar answered Oct 05 '22 08:10

John Dibling


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))
like image 34
EvilTeach Avatar answered Oct 05 '22 06:10

EvilTeach


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; })
like image 26
dalle Avatar answered Oct 05 '22 06:10

dalle


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.

like image 41
DarthPingu Avatar answered Oct 05 '22 06:10

DarthPingu


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)
like image 42
sanjoyd Avatar answered Oct 05 '22 07:10

sanjoyd


Undefine your macros.

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

like image 23
lillq Avatar answered Oct 05 '22 08:10

lillq


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.

like image 29
Mike Dunlavey Avatar answered Oct 05 '22 08:10

Mike Dunlavey