Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/MicrosoftDocs/cpp-docs/llms.txt

Use this file to discover all available pages before exploring further.

Preprocessor directives tell the preprocessor to perform specific actions before the compiler sees the source text. They begin with a # sign as the first non-whitespace character on a line and are terminated by the end of that line (use a trailing backslash \ to continue a directive across multiple lines). Directives are distinct from C statements — they are processed in a completely separate phase of compilation and have no knowledge of types, scopes, or run-time values. The number sign (#) must be the first non-white-space character on the line containing the directive. White-space characters can appear between the number sign and the first letter of the directive name.

#include — File Inclusion

The #include directive causes the preprocessor to replace the directive with the entire contents of the named file. It supports two forms that differ in where the preprocessor searches for the file:
#include <filename>   /* angle-bracket form — system/library headers */
#include "filename"   /* quoted form       — project-local headers   */

Search Order

When you use #include "filename", the preprocessor searches in this order:
  1. The directory containing the file with the #include statement (the parent file’s directory).
  2. Directories of currently open parent include files (grandparent directories), in reverse-open order.
  3. Directories specified by each /I compiler option (in order).
  4. Directories specified by the INCLUDE environment variable.
/* In src/network/socket.c — looks first in src/network/ */
#include "socket_internal.h"

Nested Includes

Include files can be nested up to 10 levels deep. When a nested #include finishes processing, the preprocessor continues inserting the enclosing parent file:
/* myapp.h */
#include <stddef.h>     /* level 1 */
#include "platform.h"   /* level 1 — platform.h may include more headers */
If you provide a fully-qualified path in the #include directive, the preprocessor uses that path directly and ignores the standard search order: #include "C:\\SDK\\include\\sdk_api.h".

Practical Example — Header Organization

/* main.c */
#include <stdio.h>          /* C standard I/O */
#include <stdlib.h>         /* malloc, free, exit */
#include <string.h>         /* string functions */

#include "config.h"         /* project build configuration */
#include "logger.h"         /* project logging API */
#include "network/http.h"   /* HTTP subsystem */

int main(void) {
    logger_init(LOG_LEVEL_INFO);
    printf("App version: %s\n", APP_VERSION); /* defined in config.h */
    return 0;
}

#define and #undef — Macro Definition

The #define directive creates a macro, which the preprocessor uses to perform textual substitution throughout the source file. Once defined, every occurrence of the macro name is replaced with its token string.

Object-Like Macros

An object-like macro has no parameters. Use it to define symbolic constants:
#define PI           3.14159265358979323846
#define MAX_PATH     260
#define BUFFER_SIZE  (4 * 1024)         /* parentheses ensure correct precedence */
#define APP_NAME     "MyApplication"

double area   = PI * radius * radius;
char   path[MAX_PATH];
char   buf[BUFFER_SIZE];
Always wrap macro constant expressions in parentheses when they involve arithmetic operators. Without parentheses, the macro may expand unexpectedly:
#define SIZE   2 + 2        /* dangerous */
int arr[SIZE * 3];          /* expands to arr[2 + 2 * 3] = arr[8], not arr[12] */

#define SIZE   (2 + 2)      /* safe */
int arr[SIZE * 3];          /* expands to arr[(2+2)*3] = arr[12] */

Removing a Definition with #undef

#undef removes a macro definition. After #undef, the identifier is no longer defined and can be redefined to a different value:
#define BUFFER_SIZE 1024
/* ... use BUFFER_SIZE ... */

#undef  BUFFER_SIZE
#define BUFFER_SIZE 4096    /* redefine to larger value */

#if, #ifdef, #ifndef, #elif, #else, #endif — Conditional Compilation

Conditional directives allow portions of source code to be compiled only when specific conditions are true. They are the primary mechanism for platform portability, feature flags, and debug builds.

Basic Structure

#if    constant-expression      /* include if expression is non-zero */
    /* ... */
#elif  other-expression         /* else-if chain (zero or more) */
    /* ... */
#else                           /* optional fallback */
    /* ... */
#endif                          /* required closing directive */
Every #if, #ifdef, or #ifndef must be paired with a matching #endif.

#ifdef and #ifndef

#ifdef IDENTIFIER is equivalent to #if defined(IDENTIFIER). #ifndef IDENTIFIER is equivalent to #if !defined(IDENTIFIER).
/* Classic include guard using #ifndef */
#ifndef MY_HEADER_H
#define MY_HEADER_H

/* header content here — only processed once per translation unit */
typedef struct { int x, y; } Point;

#endif /* MY_HEADER_H */

Practical Examples

#ifdef _WIN32
#  include <windows.h>
#  define PATH_SEP '\\'
#elif defined(__linux__)
#  include <unistd.h>
#  define PATH_SEP '/'
#elif defined(__APPLE__)
#  include <TargetConditionals.h>
#  define PATH_SEP '/'
#else
#  error "Unsupported platform"
#endif

The defined Operator

The defined operator can be used inside #if and #elif expressions. It evaluates to 1 if the identifier is defined, 0 otherwise:
#if defined(CREDIT)
    credit();
#elif defined(DEBIT)
    debit();
#else
    printerror();
#endif

/* Combine with logical operators */
#if defined(_MSC_VER) && _MSC_VER >= 1900
#  pragma comment(lib, "legacy_stdio_definitions.lib")
#endif

#line — Line Number and Filename Override

The #line directive overrides the line number (and optionally filename) that the compiler uses in diagnostic messages. It is typically generated by tools like parser generators, IDL compilers, and template engines so that errors point to the original source file rather than the generated file.
/* Syntax */
#line digit-sequence
#line digit-sequence "filename"

/* Example */
#line 100 "original_source.c"
int x = undefined_variable;  /* error reported as original_source.c(100) */
The __LINE__ and __FILE__ predefined macros reflect the values set by #line:
#include <stdio.h>

int main(void) {
    printf("File: %s, Line: %d\n", __FILE__, __LINE__);
    /* Output: File: line_directive.cpp, Line: <actual line> */

#line 10 "hello.cpp"
    printf("File: %s, Line: %d\n", __FILE__, __LINE__);
    /* Output: File: hello.cpp, Line: 10 */
    return 0;
}

#error and #warning — Compile-Time Diagnostics

#error

The #error directive emits a user-specified error message and terminates compilation. Use it to enforce build prerequisites:
/* Require a 32-bit or 64-bit int */
#if UINT_MAX < 0xFFFFFFFF
#error This code requires at least a 32-bit int type.
#endif

/* Require a C++ compiler for C++ headers */
#ifndef __cplusplus
#error This header requires a C++ compiler.
#endif

/* Ensure a required feature flag is set */
#if !defined(PLATFORM_WINDOWS) && !defined(PLATFORM_LINUX)
#error You must define either PLATFORM_WINDOWS or PLATFORM_LINUX.
#endif

#warning

The #warning directive emits a non-fatal diagnostic. Compilation continues after #warning:
/* Warn about deprecated usage */
#if defined(USE_OLD_API)
#warning USE_OLD_API is deprecated. Migrate to the new API by v3.0.
#endif

/* Warn about missing optional feature */
#ifndef HAS_HARDWARE_AES
#warning Hardware AES acceleration is unavailable; using software fallback.
#endif
The #warning directive is a common extension. It is supported by MSVC, GCC, and Clang. The token-string argument to both #error and #warning is not subject to macro expansion — it is emitted literally.

#pragma — Compiler-Specific Instructions

The #pragma directive passes implementation-specific instructions to the compiler. Unlike other preprocessor directives, pragmas are not removed from the translation unit — they are interpreted by the compiler itself. If the compiler does not recognize a pragma, it issues a warning and continues.
#pragma once                             /* header include guard */
#pragma warning(disable : 4996)          /* suppress C4996 deprecation warning */
#pragma comment(lib, "ws2_32.lib")       /* link Winsock automatically */
#pragma pack(push, 1)                    /* 1-byte struct member alignment */
struct PackedStruct { char a; int b; };  /* no padding bytes */
#pragma pack(pop)                        /* restore previous alignment */
The __pragma keyword (Microsoft-specific) and the standard _Pragma operator (C99+) allow pragmas inside macro definitions:
/* _Pragma — standard C99 form */
#define SUPPRESS_WARNING(n) _Pragma(#n)
SUPPRESS_WARNING(warning(disable : 4996))

/* __pragma — Microsoft-specific, usable in macros */
#define BEGIN_PACKED __pragma(pack(push, 1))
#define END_PACKED   __pragma(pack(pop))

BEGIN_PACKED
struct NetworkHeader {
    unsigned short type;
    unsigned int   length;
};
END_PACKED
For a complete reference of MSVC pragma directives, see Pragma Directives.

Line Continuation

Any preprocessor directive can span multiple source lines by placing a backslash (\) immediately before the newline:
#define LONG_MACRO(a, b, c)  \
    do {                      \
        step_one(a);          \
        step_two(b);          \
        step_three(c);        \
    } while (0)

See Also

Build docs developers (and LLMs) love