← Back to C++ Course | Chapter 16: Modern C++ | Lesson 9 of 9

C++ Preprocessor & Macros

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?

cpp
#include <iostream>

#define GREETING "Hello"

int main() {
	std::cout << GREETING << std::endl;
	return 0;
}

#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

cpp
#include <iostream>

#define MAX_SCORE 100

int main() {
	std::cout << MAX_SCORE << std::endl;
	return 0;
}

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

cpp
#include <iostream>

#define SQUARE(x) ((x) * (x))

int main() {
	std::cout << SQUARE(5) << std::endl;
	return 0;
}

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

cpp
#include <iostream>

#define DEBUG_MODE

int main() {
#ifdef DEBUG_MODE
	std::cout << "Debug build" << std::endl;
#else
	std::cout << "Release build" << std::endl;
#endif
	return 0;
}

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

cpp
#include <iostream>

#define SQUARE_BAD(x) x*x
#define SQUARE_GOOD(x) ((x) * (x))

int main() {
	std::cout << SQUARE_BAD(1 + 2) << std::endl;
	std::cout << SQUARE_GOOD(1 + 2) << std::endl;
	return 0;
}

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.