Preprocessor Directives in C++
In this lesson: C++ preprocessor directives — #include, #define, #ifdef, #ifndef, #pragma — with examples. In Hindi & English.
What is the preprocessor?
Before the compiler sees your code, a program called the preprocessor reads it and follows every line that starts with #. It copies header files in, replaces macro names with their text, and removes blocks of code you have turned off. The result is plain C++ that the compiler then translates.
#, occupy one line, and do not end with a semicolon.Directives at a glance
| Directive | Purpose |
|---|---|
#include | Insert the contents of another file |
#define | Define a macro (a name replaced by text) |
#undef | Remove a macro definition |
#if, #elif, #else, #endif | Keep or skip code based on a condition |
#ifdef, #ifndef | Keep code only if a macro is (not) defined |
#error | Stop compilation with a message |
#pragma | Compiler-specific instruction, e.g. #pragma once |
#line | Change reported line numbers (rare) |
#include
#include <iostream> // standard header #include "marks.h" // your own header
See header files for the difference between < > and " " and how include guards work.
#define: object-like macros
#include <iostream>
#define SCHOOL "Alpine Public School"
#define MAX_STUDENTS 40
int main() {
std::cout << SCHOOL << " allows " << MAX_STUDENTS << " students per class.\n";
return 0;
}Every MAX_STUDENTS is replaced by 40 before compilation. In C++ a constexpr constant is usually better because it has a type — see constants and literals.
Function-like macros and their traps
#define SQUARE(x) x * x int a = SQUARE(3 + 1); // becomes 3 + 1 * 3 + 1 -> 7, not 16!
Always wrap the parameter and the whole body in parentheses:
#define SQUARE(x) ((x) * (x))
Even then, SQUARE(i++) increments i twice. A constexpr function has none of these problems:
constexpr int square(int x) { return x * x; }Conditional compilation
Code between #if/#ifdef and #endif is compiled only when the condition is true. A common use is debug output you can switch off in one place:
#include <iostream>
#define DEBUG 1
int main() {
int total = 0;
for (int m : {78, 85, 69}) {
total += m;
#if DEBUG
std::cout << "[debug] added " << m << ", total = " << total << "\n";
#endif
}
std::cout << "Total: " << total << "\n";
return 0;
}Change DEBUG to 0 and the debug lines disappear from the compiled program entirely. You can also define a macro from the command line: g++ -DDEBUG=1 main.cpp.
Platform-specific code uses predefined macros:
#ifdef _WIN32
system("cls");
#else
system("clear");
#endif#ifndef include guards and #pragma once
#ifndef MARKS_H #define MARKS_H // ... declarations ... #endif
The first time the header is included MARKS_H is not defined, so the content is used and MARKS_H gets defined. Any later include skips it. #pragma once does the same job in one line on all major compilers.
#undef and #error
#define LIMIT 10 #undef LIMIT // LIMIT no longer exists #if __cplusplus < 201703L #error "This program needs C++17 or newer" #endif
#error is useful to stop a build early with a clear message instead of confusing errors later.
Predefined macros
| Macro | Value |
|---|---|
__LINE__ | Current line number |
__FILE__ | Current file name |
__DATE__, __TIME__ | Date and time of compilation |
__cplusplus | C++ standard version, e.g. 201703L for C++17 |
std::cout << "Compiled on " << __DATE__ << " (line " << __LINE__ << ")\n";
Common mistakes
- Adding a semicolon to a
#define(#define MAX 100;). - Missing parentheses in function-like macros.
- Forgetting
#endif, which gives "unterminated #if" errors. - Using macros where
constexpr,inlinefunctions or templates are clearer and type-safe. - Spaces between the macro name and
(:#define SQ (x) ((x)*(x))defines an object-like macro, not a function-like one.
Write a program with a DEBUG switch that prints each step of a factorial loop only when DEBUG is 1. Then compile it once with -DDEBUG=0 and once with -DDEBUG=1 (remove the #define from the file) and compare the output.
Summary
- The preprocessor runs before compilation and handles lines starting with
#. #includeinserts files;#definecreates text-replacement macros.#if/#ifdef/#ifndef ... #endifkeep or drop code — used for debug code, platforms and include guards.- Directives never end with a semicolon.
- Prefer
constexprandinlinefunctions over macros in C++.
Related: header files, inline functions, constants and literals.
Frequently Asked Questions
What is a preprocessor directive in C++?
# that is processed before compilation. The preprocessor uses these lines to include files, replace macro names with text, and keep or remove blocks of code. The compiler then sees only the resulting text.What is the difference between a macro and a function in C++?
constexpr or inline functions over function-like macros.What is conditional compilation?
#if, #ifdef, #ifndef, #elif, #else and #endif to include or skip code before compiling. It is used for include guards, debug-only code, and code that differs between Windows and Linux.Does a preprocessor directive end with a semicolon?
#define MAX 100; puts the semicolon into the replacement text, so int a[MAX]; becomes int a[100;]; and fails to compile.