C Programming 3 min read

The preprocessor and macros

The preprocessor edits your source as text before the compiler sees it. #define replaces names, function-like macros paste their arguments in unevaluated, so they need parentheses and must not get arguments with side effects. #if keeps or drops whole blocks, which is how one source builds for several boards.

Left, without parentheses: BAD_SQUARE(1 + 2) expands to 1 + 2 * 1 + 2, which evaluates to 5 because multiplication binds first. Right, with parentheses: SQUARE(1 + 2) expands to ((1 + 2) * (1 + 2)), which is 9.
A macro pastes text: without parentheses, 1 + 2 squared becomes 5.

Text replacement

Every line starting with # is handled before compilation. #include pastes in a file, and #define BUFFER_SIZE 64 replaces each later BUFFER_SIZE with 64. A function-like macro does the same with arguments: it substitutes the argument's text, not its value. BAD_SQUARE(1 + 2) becomes 1 + 2 * 1 + 2, which is 5. Parenthesise every argument and the whole body, as SQUARE does.

macros.cC
#include <stdio.h>#define BAD_SQUARE(x)  x * x#define SQUARE(x)      ((x) * (x))#define BUFFER_SIZE    64#ifndef BOARD_REV#define BOARD_REV 2               /* default; override with -DBOARD_REV=1 */#endif_Static_assert(BUFFER_SIZE % 16 == 0, "BUFFER_SIZE must be a multiple of 16");int main(void) {    printf("BAD_SQUARE(1 + 2) = %d\n", BAD_SQUARE(1 + 2));    printf("SQUARE(1 + 2)     = %d\n", SQUARE(1 + 2));#if BOARD_REV >= 2    puts("board rev 2: LED on pin 5");#else    puts("board rev 1: LED on pin 3");#endif    return 0;}
Output
BAD_SQUARE(1 + 2) = 5
SQUARE(1 + 2)     = 9
board rev 2: LED on pin 5

Conditional compilation

#if, #ifdef and #ifndef keep or remove whole blocks of code. The example picks an LED pin by board revision; building with -DBOARD_REV=1 selects the other branch without editing the file. The same mechanism provides include guards, debug-only code and per-chip register definitions. _Static_assert, new in C11, checks a constant condition at compile time and stops the build with a message when it fails.