C++
Preprocessor & Macros
10 question(s)
What is the C++ preprocessor?
Beginner
The preprocessor is a text-processing step that runs before compilation, handling directives beginning with # : #include (textual inclusion of headers), #define (macros), and conditional compilation (#if/#ifdef/#endif). It manipulates source text, not C++ semantics, so it has no knowledge of types or scope.
#include <vector> // textual inclusion
#define PI 3.14159 // macro substitution
Real-world example
The preprocessor stitches headers into each translation unit before the compiler proper runs.
Common follow-ups: When does the preprocessor run? | Does it understand C++ types?
Compilation & Linking
Preprocessor & Macros
Preprocessor & Macros
What is the difference between #include with angle brackets and quotes?
Beginner
#include <name> searches the system/standard include directories (used for standard and library headers). #include "name" searches the current directory first (then the system paths), used for your project's own headers. The distinction guides where the compiler looks for the file.
#include <string> // system/library header
#include "myclass.h" // project header
Real-world example
Project headers use quotes so the compiler finds them relative to the source, while std headers use angle brackets.
Common follow-ups: Where does each form search? | Which is for your own headers?
Compilation & Linking
Preprocessor & Macros
Preprocessor & Macros
What is an include guard and why is it needed?
Beginner
An include guard prevents a header's contents from being included more than once in a translation unit, which would cause redefinition errors. It uses #ifndef/#define/#endif around the header body, or the non-standard-but-widely-supported #pragma once. Guards are essential because headers are often included transitively multiple times.
#ifndef MYHEADER_H
#define MYHEADER_H
// ... declarations ...
#endif
Real-world example
Without an include guard, a widely-included header causes 'redefinition' errors when pulled in twice.
Common follow-ups: What does #pragma once do? | Why can a header be included multiple times?
Compilation & Linking
Preprocessor & Macros
Preprocessor & Macros
Why are function-like macros discouraged in modern C++?
Intermediate
Function-like macros do blind text substitution with no type checking, no scope, and pitfalls like multiple argument evaluation and operator-precedence bugs. Modern C++ replaces them with inline functions, templates, and constexpr, which are type-safe, respect scope, and debuggable. Macros remain only for things the language can't do (conditional compilation, stringizing).
#define SQ(x) ((x)*(x))
SQ(i++) // i incremented twice! use a constexpr function instead
Real-world example
A MAX macro double-evaluates its arguments causing a subtle bug; a template function fixes it.
Common follow-ups: What is the double-evaluation problem? | What replaces macros?
Constexpr & Compile-Time
Templates
Preprocessor & Macros
What are conditional compilation directives used for?
Intermediate
#if/#ifdef/#ifndef/#else/#endif include or exclude code at compile time based on macros—used for platform-specific code, feature toggles, debug/release differences, and include guards. They let one codebase compile differently for different targets or configurations.
#ifdef _WIN32
// Windows-specific code
#else
// POSIX code
#endif
Real-world example
Platform-specific file paths are selected via #ifdef _WIN32 so one codebase builds on Windows and Linux.
Common follow-ups: What is a common use of #ifdef? | How do feature toggles use it?
Compilation & Linking
Preprocessor & Macros
Preprocessor & Macros
What are the # (stringize) and ## (token paste) operators?
Advanced
In a macro, # turns a parameter into a string literal (stringizing), and ## concatenates two tokens into one (token pasting). They enable macro tricks like generating names or debug output, but are advanced and easy to misuse. They're used in logging/assert macros and code-generation patterns.
#define STR(x) #x // STR(hi) -> "hi"
#define CAT(a,b) a##b // CAT(foo,bar) -> foobar
Real-world example
An assert-style macro uses # to embed the failing expression's text in the error message.
Common follow-ups: What does # do? | What does ## do?
Preprocessor & Macros
Strings & string_view
Preprocessor & Macros
What are predefined macros like __LINE__ and __FILE__?
Beginner
The preprocessor provides built-in macros: __LINE__ (current line number), __FILE__ (source file name), __DATE__/__TIME__ (build date/time), __func__ (function name), and __cplusplus (the C++ standard version). They're commonly used in logging, assertions, and diagnostics to report where events occurred.
std::cout << __FILE__ << ":" << __LINE__ << "\n";
Real-world example
A logging macro embeds __FILE__ and __LINE__ so each message shows where it was emitted.
Common follow-ups: What does __cplusplus indicate? | Where are these used?
Preprocessor & Macros
Compilation & Linking
Preprocessor & Macros
How does #pragma once compare to traditional include guards?
Advanced
#pragma once tells the compiler to include a file only once, achieving the same effect as #ifndef guards with less boilerplate and no risk of guard-name collisions. It's non-standard but supported by all major compilers. Its rare edge case is with the same file reachable via different paths/symlinks; traditional guards are fully portable.
#pragma once // top of header, instead of #ifndef guards
Real-world example
Most modern headers use #pragma once for brevity, falling back to guards only where strict portability is required.
Common follow-ups: What is the advantage of #pragma once? | What is its edge case?
Compilation & Linking
Preprocessor & Macros
Preprocessor & Macros
What is the difference between a macro constant and a constexpr/const constant?
Intermediate
A #define constant is untyped preprocessor text substitution with no scope, no type checking, and no debug symbol. A const/constexpr constant is a typed, scoped variable known to the compiler and debugger, participating in overload resolution and type safety. Modern C++ strongly prefers constexpr/const over #define for constants.
#define MAX 100 // untyped text
constexpr int Max = 100; // typed, scoped, debuggable
Real-world example
Replacing #define constants with constexpr gives type safety and lets them appear in the debugger.
Common follow-ups: Why prefer constexpr over #define? | Do macros have scope?
Constexpr & Compile-Time
Preprocessor & Macros
Preprocessor & Macros
What are the dangers of macros affecting names and how do you mitigate them?
Advanced
Because macros do global text replacement ignoring scope, a macro like #define max(...) can clobber legitimate identifiers (e.g., std::max, member functions), causing baffling errors. Mitigations: avoid macros where possible, use ALL_CAPS naming to signal macros, #undef them promptly, and be wary of platform headers (like windows.h) that define common names.
// windows.h defines min/max macros that break std::min/std::max
#define NOMINMAX // before including windows.h
Real-world example
windows.h's min/max macros break std::min until NOMINMAX is defined, a classic macro-name collision.
Common follow-ups: Why do macros ignore scope? | How do you prevent macro name clashes?
Namespaces
Compilation & Linking
Preprocessor & Macros