The Preprocessor and Macros

#include, #define, macro pitfalls, conditional compilation and predefined macros.

Before the compiler proper sees your code, the preprocessor runs. It handles lines starting with #: it pastes in header files, replaces macros with their definitions, and keeps or removes code depending on conditions. It works on text, not on C values, which is the source of many tricky GATE questions.

main.cyour codepreprocessor#include, #definecompilerto assembly / object codelinkerjoins librariesprogramexecutable
How a C program is built

You can see the preprocessed text with gcc -E main.c.

Object-like and function-like macros

#include <stdio.h>

#define PI 3.14159
#define MAX_STUDENTS 60
#define SQUARE_BAD(x) x * x
#define SQUARE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))

int main(void) {
    printf("%.2f\n", PI * 2 * 2);
    printf("%d\n", SQUARE_BAD(2 + 3));   /* expands to 2 + 3 * 2 + 3 */
    printf("%d\n", SQUARE(2 + 3));       /* expands to ((2 + 3) * (2 + 3)) */
    printf("%d\n", MAX(7, MAX_STUDENTS));
    return 0;
}
Output
12.57
11
25
60

Because a macro is pure text substitution, SQUARE_BAD(2 + 3) becomes 2 + 3 * 2 + 3, which is 11, not 25. Always put brackets around every parameter and around the whole body.

Stringizing and token pasting

Inside a macro, #x turns an argument into a string literal, and a ## b glues two tokens into one.

#include <stdio.h>

#define SHOW(expr) printf(#expr " = %d\n", expr)
#define MAKE_VAR(n) var_ ## n

int main(void) {
    int MAKE_VAR(1) = 10, MAKE_VAR(2) = 20;   /* declares var_1 and var_2 */
    SHOW(var_1 + var_2);
    SHOW(7 * 6);
    return 0;
}
Output
var_1 + var_2 = 30
7 * 6 = 42

Conditional compilation

#if, #ifdef, #ifndef, #else and #endif keep or drop code before compilation. This is used for debug output, platform-specific code, and include guards that stop a header from being included twice.

#include <stdio.h>

#define DEBUG 1
#define VERSION 3

int main(void) {
#if DEBUG
    printf("debug mode is on\n");
#endif

#if VERSION >= 2
    printf("using the new algorithm\n");
#else
    printf("using the old algorithm\n");
#endif

#ifndef MAX_USERS
#define MAX_USERS 100
#endif
    printf("MAX_USERS = %d\n", MAX_USERS);
    printf("compiled from %s, line %d\n", "main.c", __LINE__);
    return 0;
}
Output
debug mode is on
using the new algorithm
MAX_USERS = 100
compiled from main.c, line 21
text
/* point.h with an include guard */
#ifndef POINT_H
#define POINT_H
typedef struct { int x, y; } Point;
#endif

__LINE__, __FILE__, __DATE__ and __func__ are predefined identifiers that are handy in error messages.

GATE-style practice questions

GATE-styleNAT · numerical answer

What does the following program print?

#include <stdio.h>
#define CUBE(x) x * x * x
int main(void) {
    int a = 3;
    printf("%d\n", CUBE(a + 1));
    return 0;
}
Show answer and explanationAnswer: 10

The macro expands as text to a + 1 * a + 1 * a + 1 = 3 + 3 + 3 + 1 = 10, not 64. Bracketing as ((x) * (x) * (x)) would give 64.

GATE-styleNAT · numerical answer

What does the following program print?

#include <stdio.h>
#define DOUBLE(x) (x + x)
#define HALF(x) x / 2
int main(void) {
    printf("%d\n", DOUBLE(4) * HALF(8 + 2));
    return 0;
}
Show answer and explanationAnswer: 65

DOUBLE(4) expands to (4 + 4) and HALF(8 + 2) to 8 + 2 / 2, so the whole expression is (4 + 4) * 8 + 2 / 2. Multiplication and division come before addition: 8 × 8 = 64 and 2 / 2 = 1, giving 65. The intended answer, 8 × 5 = 40, needs brackets in HALF.

GATE-styleMCQ · one correct answer

What does the following program print?

#include <stdio.h>
#define PRODUCT(a, b) a * b
int main(void) {
    int x = 2, y = 3;
    printf("%d\n", PRODUCT(x + 1, y + 1));
    return 0;
}
Show answer and explanationAnswer: B

The expansion is x + 1 * y + 1 = 2 + 3 + 1 = 6. Only with brackets, ((a) * (b)), would it give 3 × 4 = 12.