[build::eng]
раздел 01 · теория

КАК ФАЙЛЫ СТАНОВЯТСЯ ПРОГРАММОЙ

Прежде чем доверять сборку инструментам, полезно понять, что именно они делают: четыре стадии трансляции, единицы трансляции, заголовки и правила связывания.

1.1

Единица трансляции

Компилятор не видит «проект». Он видит одну единицу трансляции (ТУ) — файл .cpp после раскрытия всех #include. Каждый .cpp компилируется независимо, а результаты сшивает линковщик.

parser.cpp

#include "parser.h"
#include <vector>
…определения функций…

→

Препроцессор

вставляет содержимое заголовков, раскрывает макросы, выполняет #ifdef

→

parser.i → parser.o

чистый текст → объектный файл с машинным кодом и таблицей символов

1.2

Четыре стадии — вручную

01

Препроцессинг

Текстовая подготовка: включения, макросы, условная компиляция.

g++ -E main.cpp -o main.i
→ main.i (развёрнутый текст, тысячи строк)
02

Компиляция

Перевод в ассемблер: синтаксис, шаблоны, перегрузки, оптимизации.

g++ -S main.i -o main.s
→ main.s (ассемблер, можно читать)
03

Ассемблирование

Инструкции становятся машинным кодом, появляется таблица символов.

g++ -c main.s -o main.o
→ main.o (объектный файл)
04

Линковка

Объекты и библиотеки соединяются: символы получают адреса.

g++ main.o util.o -o app
→ app (исполняемый файл)

Ключ -c — самый важный для систем сборки: он останавливается после ассемблирования и даёт объектный файл, который можно переиспользовать, кэшировать и линковать отдельно.

1.3

Заголовки и стражи включения

engine.h — классический guard
#ifndef ENGINE_H
#define ENGINE_H

namespace engine {
class Engine {
public:
    void start(int rpm);
};
}

#endif // ENGINE_H
engine.h — современный вариант
#pragma once

namespace engine {
class Engine;  // forward declaration
void tune(Engine& e);
}

Правило скорости: чем меньше заголовков тянет заголовок — тем быстрее сборка. Вперёд-объявляйте классы, включайте только то, что реально используете.

1.4

ODR и связывание

One Definition Rule: у каждого символа должно быть ровно одно определение на программу (для inline — сколько угодно одинаковых).

externобъявление без определения: символ живёт в другой единице трансляции и будет найден при линковке
static в .cppвнутреннее связывание: функция или переменная видна только внутри своего файла
inlineопределение может повторяться в разных ТУ — линковщик оставит одну копию
const / constexprпеременные по умолчанию имеют внутреннее связывание — в каждом .cpp своя копия
1.5

Проверь себя

Q1Что делает g++ -c main.cpp?

Q2От чего защищает #pragma once?

Q3Не-inline функция определена в двух .cpp-файлах. Что будет?

Q4Главная задача линковщика: