Fundamental C - Compilation & Preprocessor
Written by Harry Fairhead   
Monday, 17 February 2020
Article Index
Fundamental C - Compilation & Preprocessor
Macros
Conditional Compilation

Macros

You define a macro using:

#define macroname string

Anywhere the preprocessor finds macroname it replaces it by the string. It is convention to use all caps for the name of the macro to make its status clear. You can write the macro on multiple lines using the \ line break but the it is treated as if it was defined on a single line.

For example:

#define ONEHUNDRED 100
int x=ONEHUNDRED;

is expanded to:

int x=100;

You can see the expanded macro by running the preprocessor from the command line:

cpp myprogram.c

the result is an .i file that you can inspect.

If you are using NetBeans then a simpler option is to right-click on a macro and select Navigate,View Macro Expansion. This opens the Macro Expansion window where you can view the result of the expansion of all of the macros in a program.

 netbeans1

The simplest use of a macro is to define constants such as:

#define PI 3.14159

Macros take effect after they have been defined – the preprocessor reads the file from top to bottom, expanding macros as it goes.

There is also the instruction:

#undefine

which undefines a macro. This can be useful if you want the definition of a macro to apply only to part of the program or if you need different definitions in different parts of the program.

Notice that this macro replaces any use of PI by the number 3.14159 as if you had typed it into the text file. Compare this to the use of a const variable.

You can use parameters within macros to create a function macro. The parameters are a comma-separated list within parentheses. You have to write the parentheses hard against the macro’s name otherwise the parameters are taken to be part of the string that defines the macro body.

For example:

#define PI(n) 3.14159*n

If you use this in an expression it is as if you had written out the equivalent text. That is:

int x=PI(2);

is exactly equivalent to:

int x=3.14159*2;

Notice that macros don’t have any idea of data type. You can just as well write:

int x=PI(rotation);

and this would be exactly equivalent to:

int x=3.14159*rotation;

Notice that the macro is expanded as a string which is inserted into your program as if you had typed it. In this case the instruction that results is legal as long as rotation has been declared as a suitable variable earlier in the program.

 

You can have multiple parameters and you have to specify them all as a comma-separated list when you use the macro. Any parameters that you don’t specify are treated as blank. Any parameters that you include in quotes are not treated as parameters.

For example:

#define PI(n) “n times Pi”

expands to “n times Pi”, including the quotes, no matter what n is.

As long as you always think about macros as simple text substitution and nothing more you will likely not make mistakes – or rather you will understand why you make mistakes.

For example:

#define MUL(A,B) A*B

works fine in simple cases:

int x=MUL(2,3);

and expands to:

int x= 2*3;

Consider, however, what happens if you are more adventurous:

int x=MUL(a+2,b+3);

Due to operator precedence, this expands to:

int x=a+2*b+3;

which is presumably not what was intended.

This problem of operator precedence is the reason why macros often seem to have lots of extra parentheses. For example the MUL macro is better defined as:

#define MUL(A,B) (A)*(B)

The parentheses look redundant, but they make sure that whatever A is and whatever B is they are evaluated before the multiplication.

In general, many problems with macros occur because other programmers use arguments you never intended your macro to be used with.

Now we come to a feature of macros that can cause even more problems. When a macro is expanded the result is inserted into the file in place of macroname. The preprocessor then re-scans that portion of text and expands any macros it finds. The same is true if an argument contains a macro, when the macro is expanded and placed within the argument.

For example:

#define SUM(A,B) (A)+(B)

now we can write:

int x=SUM(MUL(x,y),z);

and this expands first to:

int x=SUM((x)*(y),z);

and then to:

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

which, despite having too many parentheses, is perfectly correct.

At this point you might be thinking that macros can be recursive and hence the preprocessor has the power of a full language. This is not the case. Any self reference within a macro is ignored, i.e. it is treated as text and not expanded. This also applies if the self reference is via another macro.

So if you define:

#define ADDONE ADDONE+1

the statement:

int x=ADDONE;

expands to

int x=ADDONE+1;

and not to an infinite recursion.

Finally the #, or stringizing operator, converts a parameter into a string constant, i.e. a quoted string, without expanding it. For example:

#define MKSTRING(s) #s
printf(MKSTRING(Hello World));

expands to

printf(“Hello World”);

The ## token-pasting operator allows concatenation of parameters. In this case the arguments are expanded before being concatenated. For example:

#define CREATSTRUCT(type,name) type name; type##* name##p
CREATSTRUCT(struct mystruct,myvar);

expands to:

struct mystruct myvar;
struct mystruct* myvarp;

Notice that we use ## to add the p to the end of the variable name to create a pointer.

There are also predefined macros that implement things that would be difficult without them. For example __FILE__, __LINE__ and __DATE__ expand to the current file name, line number and date respectively. The only way to find out what your system supports is to consult the manual.

Macros are useful, but they are not as powerful as you might imagine and they quickly become dangerous.

There are extensions to the basic macro syntax that are only supported by particular C standards. For example, C99 supports a variable number of parameters.

The C preprocessor’s syntax doesn’t seem to have much to do with C so it is easy to forget that it is actually defined by the C standards. The problem is that it isn’t particularly well defined by them and hence there are variations in how things work. The best advice is to keep macros simple and only to use the most common idioms.

You also will encounter unexpected problems even with simple macros – usually because of the way spaces are either inserted or not inserted, the way punctuation modifies meaning, and the range of things that can be passed as arguments.

It is worth saying that you will encounter macros in code that leave you wondering what they do and how they work. Such macros are best avoided and are not to be admired or emulated.



Last Updated ( Monday, 17 February 2020 )