C++ Preprocessor & Macros
In this page:
#define NAME value
#define MACRO(param) expression
#ifndef NAME
#define NAME
#endif
What is the Preprocessor?
The preprocessor is a distinct text-substitution pass that runs before actual compilation begins.
It scans the source file for any line beginning with a hash symbol (#) and processes those directives -- like #include, which literally inserts another file's text at that point -- before the compiler ever sees the resulting code.
Example: What is the Preprocessor?
// Include std::cout and std::cin
#include <iostream>
// Define the macro GREETING (replaced by the preprocessor)
#define GREETING "Hello"
// Program execution starts in main()
int main() {
// Print to the console with cout
std::cout << GREETING << std::endl;
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
#define Constants
#define NAME value creates a macro that the preprocessor textually replaces with value everywhere NAME appears in the file afterward.
Because this substitution happens before compilation, the compiler never even sees the macro name -- it only sees the expanded result.
Example: #define Constants
// Include std::cout and std::cin
#include <iostream>
// Define the macro MAX_SCORE (replaced by the preprocessor)
#define MAX_SCORE 100
// Program execution starts in main()
int main() {
// Print to the console with cout
std::cout << MAX_SCORE << std::endl;
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Function-like Macros
Macros can also accept parameters, letting them behave superficially like functions -- #define SQUARE(x) ((x)*(x)).
But because this is pure text substitution with no type checking or scoping, macros can't be overloaded, debugged with normal tools, or trusted the way real functions can.
Example: Function-like Macros
// Include std::cout and std::cin
#include <iostream>
// Define the macro SQUARE (replaced by the preprocessor)
#define SQUARE(x) ((x) * (x))
// Program execution starts in main()
int main() {
// Print to the console with cout
std::cout << SQUARE(5) << std::endl;
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Conditional Directives
#ifdef, #ifndef, and #endif let you include or exclude entire blocks of code based on whether a macro has been defined, which is how cross-platform code compiles different implementations for Windows versus Linux, or how header guards prevent a header from being processed twice in one translation unit.
Example: Conditional Directives
// Include std::cout and std::cin
#include <iostream>
// Define the macro DEBUG_MODE (replaced by the preprocessor)
#define DEBUG_MODE
// Program execution starts in main()
int main() {
#ifdef DEBUG_MODE
// Print to the console with cout
std::cout << "Debug build" << std::endl;
#else
// Print to the console with cout
std::cout << "Release build" << std::endl;
#endif
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Macro Parentheses
Because macro substitution is purely textual, failing to parenthesize both the macro's parameters and its overall expression can silently break operator precedence -- #define SQUARE(x) x*x computes the wrong result for SQUARE(a+b), expanding to a+b*a+b instead of the intended (a+b)*(a+b).
Example: Macro Parentheses
// Include std::cout and std::cin
#include <iostream>
// Define the macro SQUARE_BAD (replaced by the preprocessor)
#define SQUARE_BAD(x) x*x
// Define the macro SQUARE_GOOD (replaced by the preprocessor)
#define SQUARE_GOOD(x) ((x) * (x))
// Program execution starts in main()
int main() {
// Print to the console with cout
std::cout << SQUARE_BAD(1 + 2) << std::endl;
// Print to the console with cout
std::cout << SQUARE_GOOD(1 + 2) << std::endl;
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
- Writing a function-like macro without parentheses around arguments, so
SQUARE(a + 1)expands to the wrong expression. - Putting a semicolon at the end of a
#defineline, which becomes part of the replacement text and breaks expressions. - Passing an expression with side effects such as
MAX(i++, j)to a macro, which may evaluate it twice.
auto, range-basedforloops, andnullptrmake code simpler and safer.- Move semantics and rvalue references avoid unnecessary copying.
constexpr, structured bindings, C++20 modules, and the preprocessor and macros are additional modern and low-level tools.
Chapter Quiz — Complete all 9 topics to unlock
0/9 topics done
Complete these topics first: