[2026] C++ Preprocessor Directives: #include, #define, #ifdef, and More

[2026] C++ Preprocessor Directives: #include, #define, #ifdef, and More

이 글의 핵심

Guide to C++ preprocessor directives: file inclusion, macros, conditional compilation, and #pragma. When to prefer constexpr over macros and how include guards work.

What are preprocessor directives?

Preprocessor directives are commands processed before compilation. They start with # and are used for file inclusion, macro definition, conditional compilation, and more. 아래 코드는 cpp를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 코드를 직접 실행해보면서 동작을 확인해보세요.

#include <iostream>  // file inclusion
#define MAX 100      // macro definition
#ifdef DEBUG         // conditional compilation
    // debug code
#endif

Why they exist:

  • File inclusion: pull headers together
  • Conditional compilation: platform- or build-specific code
  • Macros: constants and textual substitution
  • Build configuration: debug vs release 다음은 cpp를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// ❌ Without central config: duplicated blocks
// file1.cpp
void func() {
    #ifdef DEBUG
        std::cout << "debug\n";
    #endif
}
// file2.cpp
void func2() {
    #ifdef DEBUG
        std::cout << "debug\n";
    #endif
}
// ✅ Centralized with the preprocessor
// config.h
#ifdef DEBUG
    #define LOG(x) std::cout << x << '\n'
#else
    #define LOG(x)
#endif
// file1.cpp, file2.cpp
LOG("debug");

Preprocessor pipeline: 아래 코드는 mermaid를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

flowchart LR
    A[Source code] --> B[Preprocessor]
    B --> C[Preprocessed code]
    C --> D[Compiler]
    D --> E[Assembly]
    E --> F[Linker]
    F --> G[Executable]

Preprocessor order:

  1. File inclusion (#include)
  2. Macro expansion (#define)
  3. Conditional compilation (#ifdef, #if)
  4. Other directives (#pragma, #error) Inspect preprocessor output: 아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# GCC/Clang
g++ -E file.cpp -o file.i
# MSVC
cl /E file.cpp

Main directives

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 1. #include
#include <iostream>   // system header
#include "myheader.h" // project header
// 2. #define
#define PI 3.14
#define MAX(a,b) ((a)>(b)?(a):(b))
// 3. #undef
#undef MAX
// 4. #ifdef, #ifndef
#ifdef DEBUG
    #define LOG(x) std::cout << x
#else
    #define LOG(x)
#endif
// 5. #if, #elif, #else
#if VERSION >= 2
    // version 2+
#elif VERSION == 1
    // version 1
#else
    // other
#endif
// 6. #pragma
#pragma once
#pragma pack(1)

Practical examples

Example 1: Include guards

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// myheader.h
#ifndef MYHEADER_H
#define MYHEADER_H
class MyClass {
    // ...
};
#endif
// or #pragma once
#pragma once
class MyClass {
    // ...
};

Example 2: Conditional compilation

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// config.h
#define DEBUG_MODE 1
#define PLATFORM_WINDOWS 1
// main.cpp
#include "config.h"
#if DEBUG_MODE
    #define LOG(x) std::cout << "[DEBUG] " << x << std::endl
#else
    #define LOG(x)
#endif
#ifdef PLATFORM_WINDOWS
    #include <windows.h>
#elif defined(PLATFORM_LINUX)
    #include <unistd.h>
#endif
int main() {
    LOG("program start");
}

Example 3: Macro functions

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

#define SQUARE(x) ((x) * (x))
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN(a,b) ((a) < (b) ? (a) : (b))
int main() {
    int x = SQUARE(5);        // 25
    int max = MAX(10, 20);    // 20
    int min = MIN(10, 20);    // 10
}

Example 4: Stringizing and token pasting

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

#define STRINGIFY(x) #x
#define CONCAT(a,b) a##b
int main() {
    std::cout << STRINGIFY(Hello) << std::endl;  // "Hello"
    
    int xy = 10;
    int result = CONCAT(x, y);  // xy
}

Conditional compilation

다음은 cpp를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// By platform
#ifdef _WIN32
    // Windows
#elif defined(__linux__)
    // Linux
#elif defined(__APPLE__)
    // macOS
#endif
// By compiler
#ifdef __GNUC__
    // GCC
#elif defined(_MSC_VER)
    // MSVC
#endif
// Debug vs release
#ifdef NDEBUG
    // release
#else
    // debug
#endif

Common pitfalls

Pitfall 1: Macro side effects

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// ❌ Side effects
#define SQUARE(x) x * x
int result = SQUARE(1 + 2);  // 1 + 2 * 1 + 2 = 5
// ✅ Parentheses
#define SQUARE(x) ((x) * (x))
int result = SQUARE(1 + 2);  // 9

Pitfall 2: Missing include guards

아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.

// ❌ No guard
// myheader.h
class MyClass {};
// ✅ Add guards
#ifndef MYHEADER_H
#define MYHEADER_H
class MyClass {};
#endif

Pitfall 3: Macros vs functions

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// ❌ Macro (not type-safe)
#define MAX(a,b) ((a)>(b)?(a):(b))
// ✅ Template function
template<typename T>
T max(T a, T b) {
    return a > b ? a : b;
}

Pitfall 4: #pragma once vs guards

아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.

// #pragma once (short)
#pragma once
class MyClass {};
// Include guards (portable)
#ifndef MYHEADER_H
#define MYHEADER_H
class MyClass {};
#endif

#pragma directives

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 1. #pragma once
#pragma once
// 2. #pragma pack
#pragma pack(push, 1)
struct Data {
    char c;
    int i;
};
#pragma pack(pop)
// 3. #pragma warning (MSVC)
#pragma warning(disable: 4996)
// 4. #pragma message
#pragma message("compile message")
// 5. #pragma omp (OpenMP)
#pragma omp parallel for
for (int i = 0; i < 100; i++) {
    // parallel
}

Real-world patterns

Pattern 1: Platform abstraction

다음은 cpp를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// platform.h
#ifdef _WIN32
    #define EXPORT __declspec(dllexport)
    #define PATH_SEP '\\'
    #include <windows.h>
#elif defined(__linux__)
    #define EXPORT __attribute__((visibility("default")))
    #define PATH_SEP '/'
    #include <unistd.h>
#elif defined(__APPLE__)
    #define EXPORT __attribute__((visibility("default")))
    #define PATH_SEP '/'
    #include <TargetConditionals.h>
#endif
// Usage
EXPORT void myFunction() {
    std::string path = "dir" + std::string(1, PATH_SEP) + "file.txt";
}

Pattern 2: Debug logging

다음은 cpp를 활용한 상세한 구현 코드입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// debug.h
#ifdef DEBUG
    #define LOG(level, msg) \
        std::cout << "[" << level << "] " << __FILE__ << ":" << __LINE__ \
                  << " " << msg << '\n'
    #define ASSERT(cond) \
        if (!(cond)) { \
            std::cerr << "Assertion failed: " #cond << '\n'; \
            std::abort(); \
        }
#else
    #define LOG(level, msg)
    #define ASSERT(cond)
#endif
// Usage
void processData(int* data, size_t size) {
    ASSERT(data != nullptr);
    ASSERT(size > 0);
    
    LOG("INFO", "Processing " << size << " items");
    // ...
}

Pattern 3: Versioning

다음은 cpp를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// version.h
#define VERSION_MAJOR 2
#define VERSION_MINOR 3
#define VERSION_PATCH 1
#if VERSION_MAJOR >= 2
    #define HAS_NEW_FEATURE 1
#endif
// api.cpp
void useAPI() {
    #ifdef HAS_NEW_FEATURE
        newFeature();
    #else
        oldFeature();
    #endif
}

FAQ

Q1: When do I use preprocessor directives?

A:

  • File inclusion: #include to compose headers
  • Conditional compilation: platform-specific code
  • Macros: constants and textual substitution

Q2: #pragma once vs include guards?

A:

  • #pragma once: short and fast (non-standard but widely supported)
  • Include guards: standard and portable

Q3: Macros vs functions?

A:

  • Macros: preprocess-time, not type-safe, hard to debug
  • Functions: type-safe and debuggable

Q4: #ifdef vs #if defined?

A:

  • #ifdef: simple conditions
  • #if defined: compound conditions

Q5: How do I see preprocessor output?

A: g++ -E file.cpp or cl /E file.cpp.

Q6: How do I avoid macro pitfalls?

A: Use enough parentheses.

Q7: Debugging macros?

A: Use #error and #warning.

Q8: Learning resources?

A:


Practical tips

Debugging

  • Start with compiler warnings when something fails.
  • Reproduce with a small test case.

Performance

  • Do not optimize without profiling.
  • Set measurable goals first.

Code review

  • Check common review feedback early.
  • Follow team conventions.

Practical checklist

Before coding

  • Is this the right technique for the problem?
  • Can the team maintain it?
  • Does it meet performance needs?

While coding

  • All warnings fixed?
  • Edge cases handled?
  • Error handling OK?

During review

  • Intent clear?
  • Tests enough?
  • Docs adequate? Use this checklist to improve quality.

C++, preprocessor, macro, directive, include

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3