Macros

The command

#define FOO (12)

causes any occurrence of the word FOO in your source file to be replaced by (12) by the preprocessor. To count as a word, FOO can’t be adjacent to other alphanumeric characters, so for example FOOD will not expand to (12)D.

Macros with arguments

To create a macro with arguments, put them in parentheses separated by commas after the macro name, e.g.

#define Square(x) ((x)*(x))

Now if you write Square(foo) it will expand as ((foo)*(foo)). Note the heavy use of parentheses inside the macro definition to avoid trouble with operator precedence; if instead we had written

#define BadSquare(x) x*x

then BadSquare(3+4) would give 3+4*3+4, which evaluates to 19, which is probably not what we intended. The general rule is that macro arguments should always be put in parentheses if you are using them in an expression where precedence might be an issue.

Multiple arguments

You can have multiple arguments to a macro, e.g.

#define Average(x,y) (((x)+(y))/2.0)

The usual caveats about using lots of parentheses apply.

Perils of repeating arguments

Macros can have odd effects if their arguments perform side-effects. For example, Square(++x) expands to ((++x)*(++x)); if x starts out equal to 1, this expression may evaluate to any of 2, 6, or 9 depending on when the ++ operators are evaluated, and will definitely leave 3 in x instead of the 2 the programmer probably expects. For this reason it is generally best to avoid side-effects in macro arguments, and to mark macro names (e.g. by capitalization) to clearly distinguish them from function names, where this issue doesn’t come up.

Variable-length argument lists

C99 added variadic macros that may have a variable number of arguments; these are mostly useful for dealing with variadic functions (like printf) that also take a variable number of arguments.

To define a variadic macro, define a macro with arguments where the last argument is three periods: ... . The macro __VA_ARGS__ then expands to whatever arguments matched this ellipsis in the macro call.

For example:

#include <stdio.h>

#define Warning(...) fprintf(stderr, __VA_ARGS__)

int
main(int argc, char **argv)
{
    Warning("%s: this program contains no useful code\n", argv[0]);
    
    return 1;
}

It is possible to mix regular arguments with ..., as long as ... comes last:

#define Useless(format, ...) printf(format, __VA_ARGS__)

Macros vs. inline functions

It is sometimes tempting to use a macro to avoid having to retype some small piece of code that does not seem big enough to justify a full-blown function, especially if the cost of the body of the function is small relative to the cost of a function call. Inline functions are a mechanism that is standard in C99 (and found in some compilers for older variants of C) that give you the ability to write a function that will never pay this function call overhead; instead, any call to an inline function is effectively replaced by the body of the function. Unlike parameterized macros, inline functions do not suffer from issues with duplicated parameters or weird text-substitution oddities.

To take a simple example, the distSquared function that we used to illustrate function definitions doesn’t do very much: just two multiplications and an addition. If we are doing a lot of distSquared computations, we could easily double the cost of each computation with function call overhead. One alternative might be to use a macro:

#define DistSquared(x,y) ((x)*(x)+(y)*(y))

but this suffers from the parameter-duplication problem, which could be particularly unfortunate if we compute DistSquared(expensiveFunctionWithManySideEffects(), 12). A better alternative is to use an inline function.

Like macros, inline functions should be defined in header files. Ordinary functions always go in C files because (a) we only want to compile them once, and (b) the linker will find them in whatever .o file they end up in anyway. But inline functions generally don’t get compiled independently, so this doesn’t apply.

Here is a header file for an inline version of distSquared:

/* Returns the square of the distance between two points separated by 
   dx in the x direction and dy in the y direction. */
static inline int
distSquared(int dx, int dy)
{
    return dx*dx + dy*dy;
}

examples/functions/distSquaredInline.h

This looks exactly like the original distSquared, except that we added static inline. We want this function to be declared static because otherwise some compilers will try to emit a non-inline definition for it in ever C file this header is included in, which could have bad results.

The nice thing about this approach is that if we do decide to make distSquared an ordinary function (maybe it will make debugging easier, or we realize we want to be able to take its address), then we can just move the definition into a .c file and take the static inline off. Indeed, this is probably the safest thing to start with, since we can also do the reverse if we find that function call overhead on this particular function really does account for a non-trivial part of our running time (see profiling).

Macros that include other macros

One macro can expand to another; for example, after defining

#define FOO BAR
#define BAR (12)

it will be the case that FOO will expand to BAR which will then expand to (12). For obvious reasons, it is a bad idea to have a macro expansion contain the original macro name.

More specialized macros

Some standard idioms have evolved over the years to deal with issues that come up in defining complex macros. Usually, having a complex macro is a sign of bad design, but these tools can be useful in some situations.

Multiple expressions in a macro

Use the comma operator, e.g.

#define NoisyInc(x) (puts("incrementing"), (x)++)

The comma operator evaluates both of its operands and returns the value of the one on the right-hand side.

You can also choose between alternatives using the ternary ?: operator, as in

#define Max(a,b) ((a) > (b) ? (a) : (b))

(but see the warning about repeated parameters above).

Non-syntactic macros

Suppose you get tired of writing

    for(i = 0; i < n; i++) ...

all the time. In principle, you can write a macro

#define UpTo(i, n) for((i) = 0; (i) < (n); (i)++)

and then write

    UpTo(i, 10) ...

in place of your former for loop headers. This is generally a good way to make your code completely unreadable. Such macros are called non-syntactic because they allow code that doesn’t look like syntactically correct C.

Sometimes, however, it makes sense to use non-syntactic macros when you want something that writes to a variable without having to pass it to a function as a pointer. An example might be something like this malloc wrapper:

#define TestMalloc(x) ((x) = malloc(sizeof(*x)), assert(x))

(Strictly speaking, this is probably more of a “non-semantic” macro.)

Whether the confusion of having a non-syntactic macro is worth the gain in safety or code-writing speed is a judgment call that can only be made after long and painful experience. If in doubt, it’s probably best not to do it.

Multiple statements in one macro

If you want to write a macro that looks like a function call but contains multiple statements, the correct way to do it is like

#define HiHi() do { puts("hi"); puts("hi"); } while(0)

This can safely be used in place of single statements, like this:

    if(friendly) 
        HiHi();
    else
        snarl();

Note that no construct except do..while will work here. Just using braces will cause trouble with the semicolon before the else, and no other compound statement besides do..while expects to be followed by a semicolon in this way.

String expansion

Let’s rewrite NoisyInc to include the variable name:

#define BadNoisyInc2(x) (puts("Incrementing x"), x++)

Will this do what we want? No. The C preprocessor is smart enough not to expand macro parameters inside strings, so BadNoisyInc2(y) will expand to (puts("Incrementing x"), y++). Instead, we have to write

#define NoisyInc2(x) (puts("Incrementing " #x), x++)

Here #x expands to whatever the value of x is wrapped in double quotes. The resulting string constant is then concatenated with the adjacent string constant according to standard C string constant concatenation rules.

To concatenate things that aren’t strings, use the ## operator, as in

#define FakeArray(n) fakeArrayVariableNumber ## n

This lets you write FakeArray(12) instead of fakeArrayVariableNumber12. Note that there is generally no good reason to ever do this.

Where this feature does become useful is if you want to be able to refer to part of the source code of your program. For example, here is short program that includes a macro that prints the source code and value of an expression:

#include <stdio.h>

#define PrintExpr(x) (printf("%s = %d\n", #x, (x)))

int
main(int argc, char **argv)
{
    PrintExpr(2+2);
    return 0;
}

examples/macros/printExpr.c

When run, this program prints

2+2 = 4

Without using a macro, there is no way to capture the text string "2+2" so we can print it.

This sort of trickery is mostly used in debugging. The assert macro is a more sophisticated version, which uses the built-in macros __FILE__ (which expands to the current source file as a quoted string) and __LINE__ (which expands to the current source line number, not quoted) to not only print out an offending expression, but also the location of it in the source.

Big macros

Nothing restricts a macro expansion to a single line, although you must put a backslash at the end of each line to keep it going. Here is a macro that declares a specialized sorting routine for any type that supports <:

#define DeclareSort(prefix, type) \
static int \
_DeclareSort_ ## prefix ## _Compare(const void *a, const void *b) \
{ \
    const type *aa; const type *bb; \
    aa = a; bb = b; \
    if(*aa < *bb) return -1; \
    else if(*bb < *aa) return 1; \
    else return 0; \
} \
\
void \
prefix ## _sort(type *a, int n)\
{ \
    qsort(a, n, sizeof(type), _DeclareSort_ ## prefix ## _Compare); \
}

examples/macros/declareSort.h

A typical use might be

#include <stdio.h>
#include <stdlib.h>

#include "declareSort.h"

/* note: must appear outside of any function, and has no trailing semicolon */
DeclareSort(int, int)

#define N (50)

int
main(int argc, char **argv)
{
    int a[N];
    int i;

    for(i=0; i < N; i++) {
        a[i] = N-i;
    }

    int_sort(a, N);

    for(i=0; i < N; i++) {
        printf("%d ", a[i]);
    }
    putchar('\n');

    return 0;
}

examples/macros/useDeclareSort.c

Do this too much and you will end up reinventing C++ templates, which are a more or less equivalent mechanism for generating polymorphic code that improve on C macros like the one above by letting you omit the backslashes.

Conditional compilation

In addition to generating code, macros can be used for conditional compiliation, where a section of the source code is included only if a particular macro is defined. This is done using the #ifdef and #ifndef preprocessor directives. In its simplest form, writing #ifdef NAME includes all code up to the next #endif if and only if NAME is defined. Similarly, #ifndef NAME includes all code up to the next #endif if and only if NAME is not defined.

Like regular C if statements, #ifdef and #ifndef directives can be nested, and can include else cases, which are separated by an #else directive.

#include <stdio.h>
#include <assert.h>

int
main(int argc, char **argv)
{
#ifdef SAY_HI
    puts("Hi.");
#else  /* matches #ifdef SAY_HI */
#ifndef BE_POLITE
    puts("Go away!");
#else  /* matches #ifndef BE_POLITE */
    puts("I'm sorry, I don't feel like talking today.");
#endif /* matches #ifndef BE_POLITE */
#endif /* matches #ifdfe SAY_HI */

#ifdef DEBUG_ARITHMETIC
    assert(2+2 == 5);
#endif

    return 0;
}

examples/macros/ifdef.c

Defining macros on the command line

You can turn these conditional compilation directives on and off at compile time by passing the -D flag to gcc. Here is the program above, running after compiling with different choices of options:

$ gcc -DSAY_HI -o ifdef ifdef.c
$ ./ifdef
Hi.
$ gcc -DBE_POLITE -DDEBUG_ARITHMETIC -o ifdef ifdef.c
$ ./ifdef
I'm sorry, I don't feel like talking today.
ifdef: ifdef.c:18: main: Assertion `2+2 == 5' failed.
Aborted

An example of how this mechanism can be useful is the NDEBUG macro: if you define this before including assert.h, it turns every assert in your code into a no-op. This can be handy if you are pretty sure your code works and you want to speed it up in its final shipped version, or if you are pretty sure your code doesn’t work but you want to hide the evidence. (It also means you should not perform side-effects inside an assert unless you are happy with them not happening.)

Using the flag -DNAME defines NAME to be 1. If you want something else, use -DNAME=VALUE. This can be used to bake useful information into your program at compile time, and is often used to specify filenames. Below is a simple example.

#include <stdio.h>

int
main(int argc, char **argv)
{
#ifdef MESSAGE
    puts(MESSAGE);
#endif

    return 0;
}

examples/macros/message.c

$ gcc -DMESSAGE='"Hi there!"' -o message message.c
$ ./message
Hi there!

Note that we had to put an extra layer of single quotes in the command line to keep the shell from stripping off the double quotes. This is unavoidable: had we written puts("MESSAGE") in the code, the preprocessor would have recognized that MESSAGE appeared inside a string and would not have replaced it.

The #if directive

The preprocessor also includes a more general #if directive that evaluates simple arithmetic expressions. The limitations are that it can only do integer arithmetic (using the widest signed integer type available to the compiler) and can only do it to integer and character constants and the special operator defined(NAME), which evaluates to 1 if NAME is defined and 0 otherwise. The most common use of this is to combine several #ifdef-like tests into one:

#include <stdio.h>

int
main(int argc, char **argv)
{
#if VERBOSITY >= 3 && defined(SAY_HI)
    puts("Hi!");
#endif

    return 0;
}

examples/macros/if.c

Debugging macro expansions

One problem with using a lot of macros is that you can end up with no idea what input is actually fed to the compiler after the preprocessor is done with it. You can tell gcc to tell you how everything expands using gcc -E source_file.c. If your source file contains any #include statements it is probably a good idea to send the output of gcc -E to a file so you can scroll down past the thousands of lines of text they may generate.

Can a macro call a preprocessor command?

E.g., can you write something like

#define DefinePlus1(x, y)  #define x ((y)+1)
#define IncludeLib(x)      #include "lib/" #x

The answer is no. C preprocessor commands are only recognized in unexpanded text. If you want self-modifying macros you will need to use a fancier macro processor like m4.


Licenses and Attributions


Speak Your Mind

-->