Какие основные этапы обработки исходного кода выполняет компилятор C++?

«Какие основные этапы обработки исходного кода выполняет компилятор C++?» — вопрос из категории C++ Core, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Компиляция C++ кода — это многоэтапный процесс трансляции человекочитаемого кода в машинные инструкции. Основные этапы:

  1. Препроцессинг:

    • Обрабатывает директивы, начинающиеся с #.
    • Подключает содержимое заголовочных файлов (#include).
    • Раскрывает макросы (#define).
    • Выполняет условную компиляцию (#ifdef, #if).
    • На выходе — «очищенный» исходный код (можно посмотреть с помощью g++ -E source.cpp).
  2. Компиляция (в узком смысле):

    • Синтаксический и семантический анализ препроцессированного кода.
    • Генерация промежуточного представления (чаще всего — ассемблерного кода для целевой архитектуры) или объектного кода.
    • На этом этапе возникают ошибки типа «undefined identifier» или «type mismatch».
  3. Ассемблирование (часто объединено с этапом 2):

    • Перевод ассемблерного кода в машинный код, упакованный в объектный файл (.o или .obj). Этот файл содержит код, данные, но адреса многих функций и переменных ещё не разрешены.
  4. Линковка:

    • Выполняется отдельной утилитой — линкером.
    • Объединяет несколько объектных файлов и библиотек (статических .a/.lib или указаний на динамические .so/.dll).
    • Разрешает символы: находит определение для каждого объявленного имени (функции, переменной).
    • Формирует итоговый исполняемый файл или библиотеку.

Упрощённый пример команд:

# Только препроцессинг
cpp source.cpp > source.i
# Компиляция в ассемблер
g++ -S source.i -o source.s
# Ассемблирование в объектный файл
as source.s -o source.o
# Линковка (часто всё делается одной командой)
g++ source.o -o my_program