Вот ты сохранил прекрасное:
#include <iostream>
int main() {
std::cout << "Hello!";
}
Нажал Run — и через секунду всё работает.
Кажется, будто компьютер просто взял твой C++ и выполнил его.
Ага. Щас 😆
Перед запуском код проходит целый конвейер.
🧩 1. Препроцессор подготавливает почву
Все #include, #define и другие директивы обрабатываются ещё до нормальной компиляции.
Написал:
#include <iostream>
Препроцессор такой:
— Понял, сейчас притащу сюда всё необходимое 👌
В результате исходник может раздуться в огромный кусок текста.
⚙️ 2. Компилятор разбирает C++
Вот здесь начинается настоящая проверка.
Компилятор смотрит на синтаксис, типы, выражения, пытается оптимизировать код и превращает его в инструкции гораздо ближе к процессору.
Если ты где-то написал ерунду — именно здесь часто прилетает знаменитая стена ошибок на полтерминала ☠️
🔩 3. Ассемблер делает объектный файл
Получившийся низкоуровневый код превращается в машинные инструкции.
Появляется .o или .obj.
Но это ещё не готовая программа.
Там могут оставаться ссылки примерно в духе:
— Мне тут нужна функция из другого файла. Где она — понятия не имею 🤷♂️
🔗 4. Приходит линкер и собирает всё вместе
Линкер берёт объектные файлы, подключает нужные библиотеки, находит функции и связывает все куски программы.
И вот только после этого появляется тот самый исполняемый файл 🎉
Если линкер чего-то не найдёт — получишь отдельный сорт боли:
undefined reference
То есть код ты написал нормально, компилятор его съел…
а программа всё равно не собралась 😎
Короче, путь примерно такой:
C++ → препроцессор → компилятор → ассемблер → линкер → готовая программа 🚀
И когда в следующий раз IDE просто покажет тебе Build succeeded, помни:
за этой одной строчкой только что прошла маленькая промышленная революция 😆