Компиляция: что это простыми словами и как код становится программой

Компиляция переводит код, который написал программист, в машинные команды процессора.

Показываем все четыре этапа на программе из девяти строк с настоящим выводом GCC, объясняем, чем компиляция отличается от интерпретации, и разбираем частые ошибки сборки.

Статью написал:
Ваня Буявец, продюсер, основатель Checkroi
Ваня Буявец
Основатель Checkroi, продюсер, эксперт в выборе онлайн-курсов
Все 2837 статей автора Подписаться на Телеграм-канал
Одобрено экспертом:
Наташа Буявец, основатель Checkroi, эксперт по онлайн-курсам
Наташа Буявец
Основательница Checkroi, продюсер Youtube-каналов, эксперт по онлайн-курсам
Все 3491 экспертное мнение Подписаться на Телеграм-канал
Обложка: Компиляция: что это простыми словами и как код становится программой

Компиляция в программировании — перевод программы с языка, на котором её написал человек, в машинный код, который выполняет процессор. Переводит специальная программа, компилятор: на входе текстовый файл с кодом, на выходе исполняемый файл, который запускается без исходников. Чтобы выбрать и поставить компилятор для языка Си, есть отдельная инструкция: компилятор C для Windows, macOS и Linux.

Ниже разберём, что значит «скомпилировать» простыми словами, и пройдём все этапы компиляции на живой программе из девяти строк. Вывод в примерах настоящий: мы запускали команды на сервере с Ubuntu 24.04 и компилятором GCC 13.3.

У слова есть и второе значение. В литературе и музыке компиляцией называют сборник из чужих материалов или работу, составленную из заимствований, отсюда «компилятивный текст» (значение есть в Большом толковом словаре на «Грамоте»). Дальше речь только о коде.

CheckroiCheckroiПодборка курсов по C++65 курсов • 19 школСравните цены, школы, программу и найдите выгодные предложения по обучениюСравнить→

Что такое компиляция простыми словами

Рой переводит код программы в машинные команды

Процессор понимает только числа: каждая команда закодирована последовательностью байтов. Писать так вручную долго и легко ошибиться, поэтому люди пишут на Си, C++, Go или Java, а в машинные команды текст переводит компилятор.

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

Что участвует в процессе:

  • исходный код: текст программы, например файл hello.c;
  • компилятор, программа-переводчик: GCC, Clang, MSVC в Visual Studio, javac для Java;
  • объектный файл: переведённый кусок программы, который ещё не умеет запускаться;
  • исполняемый файл, то есть готовая программа: .exe в Windows, файл без расширения в Linux и macOS.

Скомпилировать программу значит прогнать её через всю эту цепочку и получить исполняемый файл. Если вы только начинаете писать код, общая картина профессии есть в материале программирование с нуля.

Этапы компиляции на примере программы на C

В документации GCC прямо сказано, что сборка может включать до четырёх стадий: препроцессинг, собственно компиляцию, ассемблирование и компоновку, всегда в этом порядке (раздел Overall Options). Обычно все четыре проходят одной командой, но каждую можно остановить флагом и посмотреть промежуточный результат.

Возьмём программу hello.c:

#include <stdio.h>

#define GREETING "Привет, компилятор!"

int main(void) {
    int answer = 42;
    printf("%s Ответ: %d\n", GREETING, answer);
    return 0;
}

Препроцессинг: 9 строк превращаются в 822

Препроцессор работает только с текстом программы: подставляет содержимое подключённых файлов и заменяет макросы на их значения. Остановиться после этого шага помогает флаг -E:

$ gcc -E hello.c -o hello.i
$ wc -l hello.i
822 hello.i

Из девяти строк получилось 822: почти всё это содержимое заголовка stdio.h. В конце файла лежит наша функция, где вместо GREETING уже стоит сама строка:

int main(void) {
    int answer = 42;
    printf("%s Ответ: %d\n", "Привет, компилятор!", answer);
    return 0;
}

Компиляция в ассемблер

Это и есть компиляция в узком смысле: компилятор разбирает код, проверяет его и переводит в команды конкретного процессора, записанные текстом. Флаг -S останавливает сборку на этом шаге и сохраняет файл hello.s. Фрагмент функции main для процессора x86-64:

main:
	pushq	%rbp
	movq	%rsp, %rbp
	subq	$16, %rsp
	movl	$42, -4(%rbp)
	...
	call	printf@PLT
	movl	$0, %eax
	leave
	ret

Наша переменная видна в строке movl $42, -4(%rbp): число 42 кладётся в память. Для процессора ARM, например в Mac на чипах Apple, ассемблер будет другим, поэтому программу на Си собирают отдельно под каждую платформу. Сам язык, на котором написан пример, можно изучить на курсах по языку C.

Ассемблирование: объектный файл

Ассемблер переводит текстовые команды в байты и пишет объектный файл. Флаг -c останавливает сборку здесь:

$ gcc -c hello.c
$ file hello.o
hello.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
$ nm hello.o
0000000000000000 T main
                 U printf

Самое интересное в выводе nm. Буква T значит, что функция main в файле есть. Буква U рядом с printf значит «используется, но не найдена»: кода этой функции в объектном файле нет, он лежит в стандартной библиотеке.

Компоновка (линковка)

Компоновщик, он же линкер, собирает объектные файлы и библиотеки в одну программу и связывает вызовы с адресами функций. После него printf находится, и файл запускается:

$ gcc hello.o -o hello
$ ./hello
Привет, компилятор! Ответ: 42

Если имя выходного файла не задать флагом -o, GCC назовёт программу a.out. Вот что получилось на каждом шаге:

Этап Команда Файл Размер
Исходный код нет hello.c 183 байта
Препроцессинг gcc -E hello.i 21 390 байт
Компиляция gcc -S hello.s 990 байт
Ассемблирование gcc -c hello.o 1 600 байт
Компоновка gcc hello.o -o hello hello 15 960 байт

Исполняемый файл в 87 раз больше исходника, хотя кода printf в нём нет: функция подтягивается из системной библиотеки при запуске. Объём занимают служебные заголовки формата ELF и таблицы для загрузчика.

В других языках компоновщик ведёт себя иначе. Go по умолчанию кладёт в исполняемый файл и свою среду выполнения, поэтому программа запускается на сервере без установленного Go. Путь в эту специальность разобран в статье как стать Go-разработчиком.

Канал основателя Checkroi Вани Буявца3 700 человек читают мой Телеграм-канал про нейросетиСобрал промпты для Claude Code и ChatGPT, разборы ИИ-инструментов и лайфхаки по продвижению бизнеса в одном местеПрисоединиться

Что происходит внутри компилятора

Фазы работы компилятора в виде конвейера

Шаг «компиляция» из схемы выше сам состоит из нескольких фаз. Это классическая схема из теории компиляции, и по ней удобно понимать, откуда берутся сообщения об ошибках. Знать её полезно любому программисту, даже если сам компилятор вы никогда писать не будете.

Фаза Что делает Что ловит
Лексический анализ Режет текст на токены: int, answer, =, 42, ; Недопустимые символы
Синтаксический анализ Строит дерево разбора по правилам языка Пропущенную точку с запятой, лишнюю скобку
Семантический анализ Проверяет смысл: типы, объявления, области видимости Сложение строки с числом, необъявленную переменную
Промежуточный код Переводит дерево в простые команды, например трёхадресный код вида t1 = a + b ничего, код уже проверен
Оптимизация Выбрасывает лишнее, считает константы заранее, упрощает циклы ничего
Генерация кода Выдаёт ассемблер под нужный процессор ничего

Оптимизацию включают флагами -O1, -O2, -O3. По умолчанию GCC собирает с -O0: так быстрее компилировать и проще отлаживать.

Отдельно стоит кросс-компиляция, когда программу собирают на одном компьютере для другого процессора. Так работает, например, программист микроконтроллеров: код пишет на ноутбуке, а прошивку собирает для чипа в устройстве.

Компиляция, интерпретация и JIT

Компилируют не все языки одинаково. Интерпретатор работает как синхронный переводчик: читает программу и выполняет её на ходу, отдельного исполняемого файла не появляется. Между этими полюсами есть промежуточные схемы.

Подход Как работает Примеры
Компиляция в машинный код Заранее, целиком, под конкретную платформу C, C++, Go, Rust
Компиляция в байт-код и JIT Компилятор делает байт-код, виртуальная машина при запуске докомпилирует частые участки в машинный код Java, Kotlin, C#
Байт-код и интерпретатор Код незаметно компилируется в байт-код, который исполняет виртуальная машина Python, Ruby

Java и Kotlin

По документации Oracle, javac читает файлы .java и компилирует их в файлы .class, которые выполняет виртуальная машина Java (справка по javac).

Один и тот же .class запускается на Windows, Linux и macOS, если там стоит JVM. На той же машине работает Kotlin, а путь в профессию разобран в статье как стать Java-разработчиком.

Python тоже компилирует

Python называют интерпретируемым, но перед выполнением он переводит функцию в байт-код. Его показывает стандартный модуль dis. Мы разобрали одну и ту же функцию в двух версиях Python:

def greet(name):
    return "Привет, " + name

# Python 3.12
LOAD_CONST               1 ('Привет, ')
LOAD_FAST                0 (name)
BINARY_OP                0 (+)
RETURN_VALUE

# Python 3.14
LOAD_CONST               0 ('Привет, ')
LOAD_FAST_BORROW         0 (name)
BINARY_OP                0 (+)
RETURN_VALUE

Команды отличаются, и это нормально. В документации модуля dis сказано, что байт-код считается внутренней деталью CPython и между версиями его могут менять. Поэтому в папке __pycache__ у каждого файла с байт-кодом в имени стоит версия, например greet.cpython-312.pyc. Скачать сам Python лучше с официального сайта, как в инструкции про python.org.

Ошибки компиляции и линковки: как их читать

Рой нашёл ошибку в коде и исправляет её

Новички часто путают два вида ошибок. Компилятор ругается на текст программы, линкер ругается на то, что не нашёл нужную функцию. Ниже настоящие сообщения GCC 13.3 и что с ними делать.

Пропущена точка с запятой

err1.c:4:23: error: expected ';' before 'return'
    4 |     printf("Привет\n")
      |                       ^
      |                       ;

Числа 4:23 означают строку и столбец. GCC даже показывает, куда вставить символ.

Решение: поставить ; в конце строки 4. Ошибка всплывает на синтаксическом анализе, объектный файл не создаётся.

Функция объявлена, но не написана

/usr/bin/ld: /tmp/ccwqztyZ.o: in function `main':
err2.c:(.text+0xe): undefined reference to `square'
collect2: error: ld returned 1 exit status

Сообщение пишет уже ld, то есть линкер. С флагом -c тот же файл компилируется без ошибок, а nm показывает U square: компилятор поверил объявлению функции, но кода её нет ни в одном файле.

Решение: дописать функцию или добавить в команду файл, где она лежит, например gcc main.c square.c -o app. Если функция из внешней библиотеки, библиотеку подключают флагом, скажем -lm для математики.

Забыли подключить заголовок

err3.c:2:5: warning: implicit declaration of function 'printf'
err3.c:1:1: note: include '<stdio.h>' or provide a declaration of 'printf'

В GCC 13 это предупреждение, и программа соберётся. Начиная с GCC 14 вызов необъявленной функции стал ошибкой, сборка остановится (заметки GCC 14 о переходе).

Решение: добавить #include <stdio.h> первой строкой. Полезно сразу собирать с флагом -Wall, чтобы видеть все предупреждения.

Правило для разбора любой ошибки: читайте первое сообщение сверху. Остальные часто его следствие и исчезают сами, когда первое исправлено.

Где освоить программирование системно

Чтобы понять компиляцию, хватит одного вечера с терминалом. Чтобы писать программы, которые собираются без ошибок и работают быстро, нужна практика с наставником и код-ревью. Курсы по C++ собраны в разделе курсы C++, по Java — в Java-разработке, ниже рейтинг программ по программированию в целом.

КурсШколаСтоимость со скидкойВ рассрочкуДлитель­ностьОбзор курса от Checkroi
Нейросети для рабочих задач
Перейти на сайт курса
SkillboxSkillbox31 290 ₽ - 1 месяцОбзор курса
Нейросети. Практический курс
Перейти на сайт курса
SkillboxSkillbox74 900 ₽ - 3 месяцаОбзор курса
Frontend-разработчик с нуля
Перейти на сайт курса
НетологияНетология113 400 ₽5385 ₽/мес.10 месяцевОбзор курса
Fullstack-разработчик на Python
Перейти на сайт курса
НетологияНетология161 200 ₽7125 ₽/мес.21 месяцОбзор курса
Профессия «Python-разработчик»
Перейти на сайт курса
SkillboxSkillbox157 335 ₽5987 ₽/мес.10 месяцевОбзор курса

Больше программ — в полном каталоге курсов по программированию

Часто задаваемые вопросы

Что значит скомпилировать программу простыми словами?

Перевести текст программы в машинный код и собрать из него исполняемый файл. После этого программа запускается сама, исходники для этого не нужны.

Чем компиляция отличается от интерпретации?

Компилятор переводит программу заранее и целиком, а интерпретатор читает и выполняет её на ходу. Java и C# используют промежуточную схему: сначала байт-код, затем докомпиляция частых участков при запуске.

Что такое объектный файл?

Переведённый в машинный код кусок программы, который ещё не связан с библиотеками. GCC создаёт его флагом -c, расширение обычно .o, в Windows .obj.

Чем ошибка компиляции отличается от ошибки линковки?

Ошибку компиляции находит компилятор в тексте программы, например пропущенную точку с запятой, и показывает строку. Ошибку линковки выдаёт компоновщик, когда не нашёл код функции: в GCC это сообщение undefined reference.

Python компилируемый язык или интерпретируемый?

Python обычно называют интерпретируемым, но перед выполнением он компилирует код в байт-код. Его можно посмотреть модулем dis, а сохраняется он в папке __pycache__.

Что значит компиляция в литературе и музыке?

Сборник из чужих материалов или работа, составленная из заимствований без собственного исследования. К программированию это значение отношения не имеет.

Читайте Checkroi первым в Google
Добавьте Checkroi в избранные источники — и наши разборы курсов и обзоры школ будут показываться выше в вашей выдаче Google.
Оставить комментарий
0 комментариев
Форма комментария

Оставьте комментарий

Напишите, что думаете. Нам важно ваше мнение!