C Preprocessor and Macros
Before the compiler sees your code, the preprocessor reads it and acts on every line that starts with #. It includes files, replaces names with values and can leave parts of the code out. It works on text, without knowing C's types or rules.
#include
#include <file.h> pastes a header from the system's libraries into your file. #include "file.h", with quotes, first looks for the file in your project. The Header Files topic uses this for your own code.
#define constants
#include <stdio.h>
#define SIZE 5
#define TITLE "Squares"
int main(void) {
int a[SIZE];
printf("%s:", TITLE);
for (int i = 0; i < SIZE; i++) {
a[i] = i * i;
printf(" %d", a[i]);
}
printf("\n");
return 0;
}Squares: 0 1 4 9 16
Function-like macros
A macro can take arguments. The preprocessor replaces the text, so put brackets around every argument and around the whole result, or operator precedence surprises you:
#include <stdio.h>
#define SQUARE_BAD(x) x * x
#define SQUARE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int main(void) {
printf("SQUARE_BAD(2 + 3) = %d\n", SQUARE_BAD(2 + 3));
printf("SQUARE(2 + 3) = %d\n", SQUARE(2 + 3));
printf("MAX(7, 12) = %d\n", MAX(7, 12));
return 0;
}SQUARE_BAD(2 + 3) = 11 SQUARE(2 + 3) = 25 MAX(7, 12) = 12
SQUARE_BAD(2 + 3) becomes 2 + 3 * 2 + 3, which is 11. Even with brackets, a macro evaluates its argument more than once: SQUARE(i++) increments i twice. In modern C, a small function (possibly marked inline) is usually the safer choice; keep macros for constants and conditional compilation.
Conditional compilation
#if, #ifdef, #ifndef, #else and #endif keep or drop code before compiling. A common use is extra output while debugging:
#include <stdio.h>
#define DEBUG 1
int main(void) {
int total = 0;
for (int i = 1; i <= 3; i++) {
total += i;
#if DEBUG
printf("[debug] i = %d, total = %d\n", i, total);
#endif
}
printf("total = %d\n", total);
return 0;
}[debug] i = 1, total = 1 [debug] i = 2, total = 3 [debug] i = 3, total = 6 total = 6
Change DEBUG to 0 and the debug lines are not even compiled. A setting can also come from the compiler command: gcc -DDEBUG=1 main.c.
Built-in macros
The preprocessor provides __FILE__ and __LINE__ (the current file name and line number), and __DATE__ and __TIME__ (when the program was compiled). They are handy in error messages: printf("error in %s line %d\n", __FILE__, __LINE__);.
Practice exercises
- Write a macro
ABS(x)for the absolute value with the right brackets, and test it withABS(-3 - 4). - Use
#ifdefto print a message only whenVERBOSEis defined, and switch it on with-DVERBOSE. - Call
SQUARE(i++)and printiafterwards. Explain the result.