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_STAMP
is 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.