DEV Community

Prathamesh Dhadbale
Prathamesh Dhadbale

Posted on

Understanding the Preprocessor in C/C++

When we compile a project, we expect all files to get compiled the way we want. But before even compilation, there is another program that runs prior to it — the preprocessor.

When the preprocessor runs, it scans for directives that start with the # symbol. Example: #include, #if, #ifdef, #ifndef, etc.

The syntax of these directives is not exactly like C or C++, though it might sometimes seem similar.


1. Defining Macros

  • Format: #define identifier replacement
  • Example: #define Table_size 100

Note: there is no semicolon to terminate — it terminates by newline.

#define and #undef — to define and undefine an identifier

#define x 100
int table1[x];   // this is a C++ code line, not the preprocessor directive
                 // that's why we terminate it with a semicolon
#undef x
#define x 200
int table2[x];
Enter fullscreen mode Exit fullscreen mode

2. Special Operators: # and ##

# (Stringizing)

The symbol # followed by a parameter name is replaced by a string literal containing the argument passed.

#define str(x) #x
Enter fullscreen mode Exit fullscreen mode

So now, every time in C++ code a pattern like str(x) is seen, it will be replaced by "x" — x inside double quotes.

## (Token pasting)

The symbol ## is used to concatenate two arguments, leaving no blank space between them.

#define stick(a,b) a##b
Enter fullscreen mode Exit fullscreen mode

So now in a C++ program, a line like:

stick(c,out) << "hi";
Enter fullscreen mode Exit fullscreen mode

...will be interpreted as:

cout << "hi";
Enter fullscreen mode Exit fullscreen mode

3. Conditional Compilation / Inclusion

#ifdef, #ifndef, #if, #endif, #else, #elif

Directive Behaviour
#ifdef Checks if a macro is defined. If yes, executes the block of code below it
#ifndef Checks if a macro is not defined
#if Followed by a condition (like in C++ code)
#else, #elif Similar behaviour to conditional statements in C++
#endif Every #if should be ended by #endif to let the preprocessor know it's closed

Example:

#if size > 200
    #undef size
    #define size 200
#elif size < 50
    #undef size
    #define size 50
#else
    #undef size
    #define size 100
#endif
Enter fullscreen mode Exit fullscreen mode

(Note: this works because size must already exist as a macro — #if evaluates the macro-expanded value.)

For understanding, in C++ we can write the above logic as:

if (size > 200) size = 200;
else if (size < 50) size = 50;
else size = 100;
Enter fullscreen mode Exit fullscreen mode

4. Predefined Macros

Macro Value
__LINE__ Int value representing the line number being compiled
__FILE__ Name of the file being compiled
__DATE__ Date of compilation, in string format "Mmm dd yyyy"
__TIME__ Time of compilation, in string format "hh:mm:ss"

These macros are used directly in a C++ program.

Example:

#include <iostream>
int main() {
    std::cout << "this is line " << __LINE__ << " of file " << __FILE__
               << "\nThis file is compiled on " << __DATE__
               << " at time " << __TIME__ << '\n';
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Output:

this is line 7 of file macros.cpp
This file is compiled on Jul 30 2026 at time 09:24:58
Enter fullscreen mode Exit fullscreen mode

5. Header Guards

One of the main applications of conditional directives is as a guard.

If a project has two or more files that import the same header file, and they're compiled together, an error of duplicate definition may occur — since the same code or function from the header file could end up used in multiple files, and when compiled into one object file, the contents from all files get added together, causing a duplicate definition.

To avoid this, the following guard is used:

#ifndef FILE_H
#define FILE_H

// write code here

#endif
Enter fullscreen mode Exit fullscreen mode

This acts as a guard and prevents multiple inclusion of the header file.

(Tip: avoid naming guards with a leading double underscore like __FILE_H — those names are reserved for the compiler/implementation. Prefer something like FILE_H instead.)

Top comments (0)