Какие знаете команды препроцессора?cplus-66

Основные директивы препроцессора

1. Директивы включения файлов

#include <filename>  // Поиск в системных каталогах
#include "filename"  // Поиск в текущей директории

Особенности:

  • Рекурсивная обработка включенных файлов
  • Поддержка угловых скобок (<>) и кавычек ("")
  • Практика: всегда используйте #pragma once в заголовочных файлах

2. Макроопределения

#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

3. Условная компиляция

#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

4. Директивы ошибок и предупреждений

#error "message"  // Остановка компиляции с ошибкой
#warning "message"  // Вывод предупреждения (не стандарт, но поддерживается)

Использование:

#if !defined(CXX_STANDARD)
#error "C++ standard version not defined"
#endif

5. Директива pragma

#pragma directive  // Зависит от реализации компилятора

Популярные варианты:

#pragma once  // Защита от множественного включения
#pragma pack(push, 1)  // Контроль выравнивания
#pragma warning(disable: 4996)  // Отключение предупреждений (MSVC)

6. Директивы для работы со строками

#line number "filename"  // Изменение номера строки и имени файла
#  // Пустая директива

Пример:

#line 42 "awesome.cpp"
// Теперь компилятор считает, что это строка 42 файла awesome.cpp

Специальные операторы в макросах

1. Оператор строки

Преобразует параметр в строковый литерал:

#define STRINGIFY(x) #x
const char* str = STRINGIFY(Hello);  // "Hello"

2. Оператор конкатенации

Объединяет токены:

#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)

Нестандартные расширения

1. Variadic макросы

#define LOG(format, ...) printf(format, __VA_ARGS__)
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)

2. Pragma operators

_Pragma("once")  // Эквивалент #pragma once

3. Директивы для asm

asm volatile ("nop");  // Вставка ассемблерного кода

Опасные практики

  1. Макросы с побочными эффектами:
#define SQUARE(x) x * x
SQUARE(i++);  // i++ * i++ - UB!
  1. Сложные вложенные макросы:
#define FOO BAR
#define BAR 42
#define STR(x) #x
STR(FOO);  // Результат "FOO", а не "42"
  1. Макросы, переопределяющие ключевые слова:
#define true false  // Абсолютное зло!

Современные альтернативы

  1. constexpr вместо #define для констант
  2. inline функции вместо функциональных макросов
  3. Модули (C++20) вместо #include
  4. Атрибуты [[deprecated]] вместо #pragma deprecated

Резюмируем: директивы препроцессора предоставляют мощные инструменты для управления компиляцией, но требуют аккуратного использования. Современный C++ предлагает более безопасные альтернативы многим традиционным техникам препроцессинга. Понимание этих директив критически важно для работы с legacy кодом и низкоуровневыми задачами.