#include <filename> // Поиск в системных каталогах
#include "filename" // Поиск в текущей директории
Особенности:
#pragma once
в заголовочных файлах#define IDENTIFIER replacement // Простой макрос
#define MACRO(param1, param2) (param1 + param2) // Макрос с параметрами
#undef IDENTIFIER // Отмена определения
Примеры:
#define PI 3.14159
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define LOG(msg) std::cout << msg << std::endl
#ifdef MACRO // Если MACRO определен
#ifndef MACRO // Если MACRO не определен
#if expression // Если expression != 0
#else // Альтернатива
#elif expression // Else if
#endif // Конец блока
Практическое применение:
#if defined(DEBUG) || defined(_DEBUG)
#define DEBUG_LOG(msg) std::cerr << msg << std::endl
#else
#define DEBUG_LOG(msg)
#endif
#error "message" // Остановка компиляции с ошибкой
#warning "message" // Вывод предупреждения (не стандарт, но поддерживается)
Использование:
#if !defined(CXX_STANDARD)
#error "C++ standard version not defined"
#endif
#pragma directive // Зависит от реализации компилятора
Популярные варианты:
#pragma once // Защита от множественного включения
#pragma pack(push, 1) // Контроль выравнивания
#pragma warning(disable: 4996) // Отключение предупреждений (MSVC)
#line number "filename" // Изменение номера строки и имени файла
# // Пустая директива
Пример:
#line 42 "awesome.cpp"
// Теперь компилятор считает, что это строка 42 файла awesome.cpp
Преобразует параметр в строковый литерал:
#define STRINGIFY(x) #x
const char* str = STRINGIFY(Hello); // "Hello"
Объединяет токены:
#define CONCAT(a, b) a ## b
int CONCAT(var, 123) = 42; // int var123 = 42;
Стандартные макросы, определенные компилятором:
__LINE__ // Текущий номер строки
__FILE__ // Имя текущего файла
__DATE__ // Дата компиляции ("MMM DD YYYY")
__TIME__ // Время компиляции ("HH:MM:SS")
__cplusplus // Версия стандарта C++ (201103L, 201402L, 201703L и т.д.)
__STDC__ // Определяется для строгого ANSI C
__func__ // Имя текущей функции (C99/C++11)
#define LOG(format, ...) printf(format, __VA_ARGS__)
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
_Pragma("once") // Эквивалент #pragma once
asm volatile ("nop"); // Вставка ассемблерного кода
#define SQUARE(x) x * x
SQUARE(i++); // i++ * i++ - UB!
#define FOO BAR
#define BAR 42
#define STR(x) #x
STR(FOO); // Результат "FOO", а не "42"
#define true false // Абсолютное зло!
constexpr
вместо #define
для константinline
функции вместо функциональных макросов#include
[[deprecated]]
вместо #pragma deprecated
Резюмируем: директивы препроцессора предоставляют мощные инструменты для управления компиляцией, но требуют аккуратного использования. Современный C++ предлагает более безопасные альтернативы многим традиционным техникам препроцессинга. Понимание этих директив критически важно для работы с legacy кодом и низкоуровневыми задачами.