Ответ
Сборка исполняемого файла или библиотеки из исходного кода на C/C++ — это многоэтапный процесс, который выполняет компилятор (например, GCC, Clang, MSVC) и компоновщик (linker).
Последовательность этапов:
-
Препроцессинг (Preprocessing)
- Что происходит: Препроцессор обрабатывает директивы, начинающиеся с
#. - Действия:
- Подключение заголовочных файлов (
#include): содержимое файлаiostreamкопируется в место включения. - Раскрытие макросов (
#define,#macro). - Условная компиляция (
#ifdef,#if,#endif). - Удаление комментариев.
- Подключение заголовочных файлов (
- Результат: Генерируется один большой промежуточный файл с расширением
.iили.ii(трансляционная единица). - Команда для просмотра:
g++ -E main.cpp -o main.ii
- Что происходит: Препроцессор обрабатывает директивы, начинающиеся с
-
Компиляция (Compilation)
- Что происходит: Компилятор преобразует препроцессированный код на C++ в ассемблерный код, специфичный для целевой архитектуры (x86, ARM).
- Ключевые моменты:
- Синтаксический и семантический анализ.
- Оптимизация кода (если указаны флаги
-O1,-O2,-O3,-Os). - Генерация ассемблерных инструкций.
- Результат: Файл на ассемблере с расширением
.s. - Команда:
g++ -S main.ii -o main.s
-
Ассемблирование (Assembly)
- Что происходит: Ассемблер преобразует человекочитаемый ассемблерный код в машинный код — объектный файл.
- Результат: Объектный файл с расширением
.o(Linux/macOS) или.obj(Windows). Он содержит машинные инструкции и таблицу символов (имена функций, переменных), но адреса многих символов еще не разрешены. - Команда:
g++ -c main.s -o main.o(или сразуg++ -c main.cpp -o main.oдля первых трех этапов).
-
Компоновка (Linking)
- Что происходит: Компоновщик (линковщик) принимает один или несколько объектных файлов и библиотек, разрешает ссылки между ними (например, вызов функции
printfиз стандартной библиотеки) и собирает итоговый исполняемый файл или библиотеку. - Типы линковки:
- Статическая: Код из библиотек (
.a,.lib) копируется прямо в исполняемый файл. Файл становится больше, но не зависит от внешних.dll/.so. - Динамическая: Исполняемый файл содержит лишь ссылки на разделяемые библиотеки (
.so,.dll). Библиотеки подгружаются во время запуска программы. Экономит память и позволяет обновлять библиотеки независимо.
- Статическая: Код из библиотек (
- Результат: Исполняемый файл (
a.out,program.exe) или библиотека (.so,.dll). - Команда:
g++ main.o utils.o -o myapp -lmylib
- Что происходит: Компоновщик (линковщик) принимает один или несколько объектных файлов и библиотек, разрешает ссылки между ними (например, вызов функции
Визуализация процесса и полезные флаги:
# Полный цикл от .cpp до исполняемого файла (скрыто)
g++ main.cpp utils.cpp -o program -O2 -std=c++17
# Раздельная компиляция (частая практика в больших проектах)
g++ -c main.cpp -o main.o -I./include # -I добавляет путь для #include
g++ -c utils.cpp -o utils.o -I./include
g++ main.o utils.o -o program -L./lib -lmylib # -L и -l для линковки библиотек
# Просмотр макросов (полезно для отладки)
g++ -dM -E main.cpp
# Просмотр зависимостей (какие библиотеки использует бинарник)
ldd program # Linux
otool -L program # macOS
Почему это важно понимать: Знание этапов помогает в отладке (например, ошибки на этапе линковки отличаются от ошибок компиляции), управлении зависимостями и настройке систем сборки (CMake, Makefile).