What is a macro, and how do you use it?


A macro is a preprocessor directive that provides a mechanism for token replacement in your source code. Macros are created by using the #define statement. Here is an example of a macro:

#define VERSION_STAMP "1.02"

The macro being defined in this example is commonly referred to as a symbol. The symbol VERSION_STAMPis simply a physical representation of the string "1.02". When the preprocessor is invoked, every occurrence of the VERSION_STAMP symbol is replaced with the literal string "1.02". Here is another example of a macro:

#define CUBE(x) ((x) * (x) * (x))

The macro being defined here is named CUBE, and it takes one argument, x. The rest of the code on the line represents the body of the CUBE macro. Thus, the simplistic macro CUBE(x) will represent the more complex expression ((x) * (x) * (x)). When the preprocessor is invoked, every instance of the macro CUBE(x)in  your program is replaced with the code ((x) * (x) * (x)).

Macros can save you many keystrokes when you are coding your program. They can also make your program much more readable and reliable, because you enter a macro in one place and use it in potentially several places. There is no overhead associated with macros, because the code that the macro represents is expanded in-place, and no jump in your program is invoked. Additionally, the arguments are not type-sensitive, so you don't have to worry about what data type you are passing to the macro.

Note that there must be no white space between your macro name and the parentheses containing the argument definition. Also, you should enclose the body of the macro in parentheses to avoid possible ambiguity regarding the translation of the macro. For instance, the following example shows the CUBE macro defined incorrectly:

#define CUBE (x) x * x * x

You also should be careful with what is passed to a macro. For instance, a very common mistake is to pass an incremented variable to a macro, as in the following example:

#include <stdio.h>
#define CUBE(x) (x*x*x)
void main(void);
void main(void)
     int x, y;
     x = 5;
     y = CUBE(++x);
     printf("y is %d\n", y);

What will y be equal to? You might be surprised to find out that y is not equal to 125 (the cubed value of 5) and not equal to 336 (6 * 7 * 8), but rather is 512. This is because the variable x is incremented while being passed as a parameter to the macro. Thus, the expanded CUBE macro in the preceding example actually appears as follows:

y = ((++x) * (++x) * (++x));

Each time x is referenced, it is incremented, so you wind up with a very different result from what you had intended. Because x is referenced three times and you are using a prefix increment operator, x is actually 8 when the code is expanded. Thus, you wind up with the cubed value of 8 rather than 5. This common mistake is one you should take note of because tracking down such bugs in your software can be a very frustrating experience. I personally have seen this mistake made by people with many years of C programming under their belts. I recommend that you type the example program and see for yourself how surprising the resulting value (512) is.

Macros can also utilize special operators such as the stringizing operator (#) and the concatenation operator (##). The stringizing operator can be used to convert macro parameters to quoted strings, as in the following example:

#define DEBUG_VALUE(v) printf(#v " is equal to %d.\n", v)

In your program, you can check the value of a variable by invoking the DEBUG_VALUE macro: ... int x = 20; DEBUG_VALUE(x); ...

The preceding code prints "x is equal to 20." on-screen. This example shows that the stringizing operator used with macros can be a very handy debugging tool.

The concatenation operator (##) is used to concatenate (combine) two separate strings into one single string.


© 2017