← Назад к вопросам

Что получается в итоге работы препроцессора?

1.3 Junior🔥 161 комментариев
#Язык C++

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Препроцессор (preprocessor) — это отдельный этап обработки исходного кода, происходящий ДО компиляции. В результате его работы получается модифицированный исходный текст на C++, готовый для компилятора.

Что делает препроцессор

Препроцессор обрабатывает директивы (строки, начинающиеся с #) и выполняет текстовые подстановки и преобразования:

  1. Включение файлов (#include)
  2. Определение макросов (#define)
  3. Условная компиляция (#if, #ifdef, #ifndef)
  4. Удаление комментариев
  5. Линеёйка строк для отладчика (#line)

Вывод препроцессора: текстовый файл

Результат работы препроцессора — это обычный текстовый файл с расширением .i (или .ii для C++), содержащий:

  • Весь раскрытый исходный код (без препроцессорных директив)
  • Раскрытые макросы
  • Включённые файлы
  • Условно скомпилированный код
// Исходный файл: main.cpp
#include <iostream>
#define MAX_SIZE 100
#define SQUARE(x) ((x) * (x))

int main() {
    int arr[MAX_SIZE];
    int result = SQUARE(5);
    std::cout << result << std::endl;
    return 0;
}

После препроцессора (main.i):

// Огромное количество содержимого из <iostream> (тысячи строк)
// ... (всё содержимое заголовочного файла iostream)
// ... объявления классов, шаблонов, встроенных функций

// Исходный файл (с раскрытыми макросами)
int main() {
    int arr[100];              // MAX_SIZE раскрыт
    int result = ((5) * (5));  // SQUARE(5) раскрыт
    std::cout << result << std::endl;
    return 0;
}

Посмотреть выход препроцессора

Можно увидеть результат работы препроцессора в разных компиляторах:

GCC/Clang

# Выполнить препроцессинг и вывести результат
g++ -E main.cpp -o main.i

# Или посмотреть в stdout
g++ -E main.cpp | head -50

# Для конкретного файла с сохранением структуры
gcc -E -P main.cpp > main.i  # -P убирает #line директивы

MSVC (Microsoft Visual C++)

# Препроцессинг
cl /E main.cpp

Реальный пример вывода

Если препроцессинг main.cpp с #include <iostream>:

$ g++ -E -P main.cpp | head -100

Будет что-то вроде:

// Начало: тысячи строк из конфигурации GCC
// ...

// Из iostream
namespace std {
  class basic_iostream { /* объявления */ };
  typedef basic_iostream<char> iostream;
  // ... ещё сотни строк
}

// Конец: исходный файл с раскрытыми макросами
int main() {
    int arr[100];
    int result = ((5) * (5));
    std::cout << result << std::endl;
    return 0;
}

Макросы: до и после

// Исходный код
#define PI 3.14159
#define PRINT_INT(x) std::cout << "Value: " << (x) << std::endl

double circumference = 2 * PI * radius;
PRINT_INT(42);

После препроцессора:

double circumference = 2 * 3.14159 * radius;
std::cout << "Value: " << (42) << std::endl;

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

// Исходный код
#ifdef DEBUG
    #define LOG(msg) std::cout << msg << std::endl
#else
    #define LOG(msg)
#endif

LOG("Debug mode");

Если компилируем с -DDEBUG:

std::cout << "Debug mode" << std::endl;

Если компилируем без -DDEBUG:

;  // пусто - макрос раскрывается в ничего

Размер файла после препроцессинга

Интересный факт: файл может значительно увеличиться:

$ ls -lh main.cpp main.i
-rw-r--r-- 1 user 2.5K main.cpp
-rw-r--r-- 1 user 1.2M main.i  # 500x больше!

Это происходит потому, что заголовочные файлы (особенно <iostream>, <vector>, <algorithm>) содержат огромное количество кода, и всё это попадает в .i файл.

Зачем это нужно

Препроцессор нужен для:

  • Повторного использования кода — включение заголовочных файлов
  • Условной компиляции — разные версии кода для разных платформ
  • Улучшения читаемости — макросы вместо дублирования кода
  • Метапрограммирования — генерация кода во время компиляции
// Платформозависимый код
#ifdef __APPLE__
    #define PLATFORM "macOS"
#elif defined(__linux__)
    #define PLATFORM "Linux"
#elif defined(_WIN32)
    #define PLATFORM "Windows"
#endif

std::cout << "Compiled for " << PLATFORM << std::endl;

Этапы компиляции в полноте

исходный файл (main.cpp)
        ↓
   [ПРЕПРОЦЕССОР]  ← выполняет # директивы
        ↓
   подготовленный текст (main.i)
        ↓
   [КОМПИЛЯТОР]    ← преобразует в assembly
        ↓
   ассемблерный код (main.s)
        ↓
   [АССЕМБЛЕР]     ← преобразует в машинный код
        ↓
   объектный файл (main.o)
        ↓
   [ЛИНКЕР]        ← объединяет с библиотеками
        ↓
   исполняемый файл (a.out)

Вывод

Результат работы препроцессора — это полностью раскрытый исходный C++ текст, где:

  • Все #include заменены содержимым файлов
  • Все макросы раскрыты и подставлены
  • Условный код обработан
  • Комментарии удалены
  • Текст готов для компилятора

Этот текст может быть сохранён в файл (.i), и его можно передать компилятору для дальнейшей обработки.