Beginner⏱️ 8 min📘 Topic 3 of 32

📎 C++ Preprocessor and Header Files — #include, #define and Header Guards

Master the C++ preprocessor — #include, #define, macros, conditional compilation, header guards and #pragma once. Learn how multi-file C++ projects link together.

Before the compiler sees your code, the preprocessor runs over it and rewrites it. Every line starting with # is a preprocessor directive.

📥 #include — pull another file's text in

#include <iostream>   // standard library, <...>
#include "utils.h"     // your own header, "..."

🧱 Splitting code: header (.h) + source (.cpp)

// math.h — DECLARATIONS
int add(int a, int b);

// math.cpp — DEFINITIONS
#include "math.h"
int add(int a, int b) { return a + b; }

// main.cpp
#include "math.h"
int main() { std::cout << add(2, 3); }

🛡️ Header guards — prevent double-inclusion

// math.h
#ifndef MATH_H
#define MATH_H
int add(int a, int b);
#endif

Or modern: #pragma once (single line, works on every major compiler).

🏷️ #define and macros

#define PI 3.14159
#define SQUARE(x) ((x) * (x))

⚠️ Macros are textual substitution — no type checking. Prefer constexpr and inline functions in modern C++.

🔀 Conditional compilation

#ifdef DEBUG
std::cout << "debug info";
#endif

💻 Code Examples

Header + source split

// greet.h
#pragma once
#include <string>
void greet(const std::string& name);

// greet.cpp
#include "greet.h"
#include <iostream>
void greet(const std::string& name) { std::cout << "Hi " << name; }

// main.cpp
#include "greet.h"
int main() { greet("Sam"); }
Output:
Compile: g++ greet.cpp main.cpp -o app

Macro pitfall — always parenthesize

#define SQUARE(x) x * x         // ❌ buggy
#define SQUARE(x) ((x) * (x))   // ✅ safe

int y = SQUARE(2 + 3);
// buggy version expands to: 2 + 3 * 2 + 3 = 11
// safe version  expands to: ((2 + 3) * (2 + 3)) = 25
Output:
Always wrap macro args and the whole macro in parens.

⚠️ Common Mistakes

  • Forgetting header guards (or #pragma once) — multiple #include of the same header causes duplicate-definition errors.
  • Putting function DEFINITIONS in a header (without inline/template) — linker errors when multiple .cpp files include it.
  • Macros without parentheses — operator precedence breaks the expansion.
  • Using `#define` for constants — prefer `constexpr` (typed, scoped, debuggable).

🎯 Interview Questions

Real questions asked at top product and service-based companies.

Q1.What is the preprocessor in C++?Beginner
A program that runs BEFORE the compiler. It processes lines starting with `#` — handling includes, macros, and conditional compilation — and produces a single text file (translation unit) the compiler then reads.
Q2.Why do we split code into .h and .cpp files?Beginner
Headers (.h) DECLARE what's available (function signatures, class definitions). Source (.cpp) DEFINES the implementation. This allows each .cpp to compile separately, then link together — keeping rebuilds fast and APIs clean.
Q3.What are header guards and why are they needed?Intermediate
Preprocessor directives (`#ifndef X / #define X / #endif`) that ensure a header's contents are included only ONCE per translation unit. Without them, including the same header twice causes redefinition errors.
Q4.What's the difference between #pragma once and #ifndef guards?Intermediate
Both prevent multiple inclusion. #pragma once is one line, simpler, and supported by every major compiler — but technically non-standard. #ifndef guards are portable to every compiler and work even in exotic build setups. Most modern projects use #pragma once.
Q5.Why prefer constexpr over #define for constants?Advanced
constexpr is typed (catches mismatches), scoped (respects namespaces), respects const semantics, shows up in debuggers, and works with templates. #define is blind textual replacement — easy to misuse, invisible to the type system.

🧠 Quick Summary

  • Preprocessor runs before the compiler; handles `#` directives.
  • Split into header (declarations) + source (definitions).
  • Always use header guards or `#pragma once`.
  • Parenthesize macro args and bodies.
  • Prefer constexpr/inline functions over macros in modern C++.