C Preprocessor, Macros and Header Guards
C Preprocessor, Macros and Header Guards
A school-wide constant should be named once. Preprocessor substitution can do that, but a careless arithmetic macro can evaluate arguments more than once.
The preprocessor handles directives such as include, define and conditional inclusion before ordinary C translation. Object-like macros substitute tokens; function-like macros do not behave like ordinary function calls.
Explain
Use a constant macro and identify repeated-evaluation hazards.
Trace
Compute the square of a value obtained by incrementing i, using the function safely.
Apply
Choose a function when a macro could repeat an argument’s side effects.
Preprocessor: follow the data
Start
A school-wide constant should be named once. Preprocessor substitution can do that, but a careless arithmetic macro can evaluate arguments more than once.
Operation
The preprocessor handles directives such as include, define and conditional inclusion before ordinary C translation. Object-like macros substitute tokens; function-like macros do not behave like ordinary function calls.
Result
Places: 2 Square: 9
Understand the operation
CLASS_CAPACITY is replaced by its defined tokens before the compiler analyzes expressions. A header guard combines #ifndef, #define and #endif to avoid reprocessing guarded declarations in one translation unit.
Parenthesizing arithmetic macro parameters prevents some precedence surprises, but a macro such as SQUARE(x) ((x) * (x)) still substitutes x twice. SQUARE(i++) can introduce unsequenced side effects. Use a function for operations that should evaluate an argument once.
Read the complete C program
This complete C17 program uses a small school example. Read declarations before the statements that use their values. Each printf format describes how the corresponding value is written to standard output.
C17 · complete program
#include <stdio.h>
#define CLASS_CAPACITY 30
static int square(int value) { return value * value; }
int main(void) {
int students = 28;
printf("Places: %d\n", CLASS_CAPACITY - students);
printf("Square: %d\n", square(3));
return 0;
}
Expected output
Places: 2
Square: 9
Run in C compiler →Follow the values through the code
C17 · code used in this explanation
#include <stdio.h>
#define CLASS_CAPACITY 30
static int square(int value) { return value * value; }
int main(void) {
int students = 28;
printf("Places: %d\n", CLASS_CAPACITY - students);
printf("Square: %d\n", square(3));
return 0;
}
The preprocessor substitutes 30 for CLASS_CAPACITY.
The function receives the value three once and calculates nine from its local parameter.
Follow the worked calculation
Read the starting values
The preprocessor substitutes 30 for CLASS_CAPACITY.
Perform the operation
The function receives the value three once and calculates nine from its local parameter.
Check the observable result
Places: 2 Square: 9
Places: 2 Square: 9
Try a small variation
C17 · code used in this explanation
#include <stdio.h>
#define CLASS_CAPACITY 30
static int square(int value) { return value * value; }
int main(void) {
int students = 28;
printf("Places: %d\n", CLASS_CAPACITY - students);
printf("Square: %d\n", square(3));
return 0;
}
Compute the square of a value obtained by incrementing i, using the function safely.
Worked answer
The argument evaluation supplies three and increments i once. The called function reuses its parameter without repeating the caller’s increment.
C17 · complete program
#include <stdio.h>
#define CLASS_CAPACITY 30
static int square(int value) { return value * value; }
int main(void) {
int i = 3;
int result = square(i++);
printf("Square: %d; next i: %d\n", result, i);
return 0;
}
Expected output
Square: 9; next i: 4
Run in C compiler →Recognize the important boundaries
A macro is token substitution, not a typed runtime parameter contract. Prefer a function when substitution offers no specific needed benefit.
An include guard does not prevent a definition copied into several separately compiled translation units from creating multiple external definitions.
Check your understanding
Choose the answer, then compare the reasoning for every option.
Use the idea independently
The preprocessor handles directives such as include, define and conditional inclusion before ordinary C translation. Object-like macros substitute tokens; function-like macros do not behave like ordinary function calls.
Continue with testing changed implementations.
Language reference: WG14 C11 committee draft. These examples use the C17-compatible subset of the explained language features.
Reaching the recap and completing the quizzes are separate milestones.