Menu
Coddy logo textTech

Что такое компилятор?

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

Автор: Kevin Spektor, Сооснователь и CTO

Обновлено 24 сентября 2026 г.

Исходный код
  1. 1print("Hello")
  2. 2total = 2 + 3
  3. 3print(total)
  4. 4print("Done")
Вывод
Компиляций0
Запусков0

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

Сохрани эту программу на C как hello.c:

#include <stdio.h>

int main(void) {
    int price = 4;
    printf("Total: %d\n", price * 3);
    return 0;
}

Затем набери gcc hello.c -o hello. На мгновение ничего не происходит, а потом рядом с исходником появляется новый файл hello. Этот файл и есть результат работы компилятора: машинный код, который может выполнить твой процессор. Удали hello.c, и ./hello всё равно работает, потому что всё, что нужно программе, уже переведено.

gcc hello.c -o hello
./hello
Total: 12

Как работает компилятор, шаг за шагом

Компилятор читает исходный код как обычный текст и обрабатывает его в несколько этапов. Возьмём одну строку total = price * 3:

  1. Лексический анализ. Текст разбивается на токены, «слова» языка: имя total, оператор =, имя price, оператор *, число 3. Пробелы и комментарии на этом этапе отбрасываются.
  2. Синтаксический анализ (парсинг). Из токенов строится синтаксическое дерево, которое фиксирует структуру: присваивание, значение которого равно произведению price и 3. Код, нарушающий грамматику языка, останавливается здесь с синтаксической ошибкой.
  3. Семантический анализ. Компилятор проверяет смысл: объявлена ли price, совместимы ли типы, существует ли вызываемая функция.
  4. Оптимизация. Программа переписывается так, чтобы работать быстрее или занимать меньше памяти, но делать то же самое. x * 2 может стать одной инструкцией сдвига, а код, который никогда не выполнится, удаляется.
  5. Генерация кода. Компилятор записывает результат для целевой платформы: машинный код x86-64 или ARM либо байт-код для виртуальной машины.

Для C и C++ есть последний шаг, компоновка (линковка): скомпилированные файлы объединяются с кодом библиотек (например, printf из стандартной библиотеки C) в один исполняемый файл. gcc запускает компоновщик за тебя, поэтому всё выглядит как одна команда.

В Python первые два этапа доступны как стандартные модули, так что можно посмотреть на их работу на настоящем примере:

NAME 'total'
OP '='
NAME 'price'
OP '*'
NUMBER '3'
Assign(
  targets=[
    Name(id='total', ctx=Store())],
  value=BinOp(
    left=Name(id='price', ctx=Load()),
    op=Mult(),
    right=Constant(value=3)))

С этим деревом и работает остальная часть компилятора. Пробелы и переносы строк ему уже неважны, важна только структура: присвоить total результат умножения price на 3.

Время компиляции: ошибки находятся до запуска программы

Компилятор читает всю программу до того, как выполнится хоть какая-то её часть, поэтому он может отвергнуть ошибки заранее. В этом примере не выполняется ни одна из трёх строк print внутри source, даже первая, потому что компилятор Python отвергает текст ещё до начала выполнения:

Rejected before running anything:
  '(' was never closed (line 3)

Компиляторы языков со статической типизацией ловят не только синтаксис. Эта строка на C в файле bad.c грамматически правильна, но записывает текст в целочисленную переменную:

int count = "five";

GCC 14 и новее останавливаются с ошибкой:

bad.c:4:17: error: initialization of 'int' from 'char *' makes integer from pointer without a cast [-Wint-conversion]

Та же ошибка в Python проявилась бы только при выполнении этой строки. Возможность поймать её во время компиляции является одной из причин, по которым большие команды выбирают компилируемые языки со статической типизацией. Но поймать всё компилятор не может: деление на число, которое оказалось нулём, или чтение несуществующего файла всё равно приводят к ошибке во время выполнения.

Примеры компиляторов

КомпиляторЧто читаетЧто создаёт
GCC, ClangC, C++Машинный код для x86-64, ARM и других
MSVC (Microsoft Visual C++)C, C++Исполняемые файлы Windows (.exe, .dll)
javacJavaБайт-код JVM (файлы .class)
rustcRustМашинный код
go buildGoОдин исполняемый файл
tscTypeScriptJavaScript

Две строки таблицы показывают, что машинный код не единственный возможный результат. javac создаёт байт-код, который выполняет виртуальная машина Java, часть среды выполнения Java. tsc превращает TypeScript в JavaScript, другой язык высокого уровня; компилятор, который делает такое, часто называют транспилятором.

Скомпилированный машинный код привязан к одному семейству процессоров и одной операционной системе. Программа, скомпилированная для Windows на x86-64, не запустится на Mac с чипом Apple серии M; тот же исходный код нужно скомпилировать заново для каждой платформы.

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

Интерпретатор выполняет исходный код напрямую, не создавая отдельного файла программы: так обычно запускают Python и Ruby. Большинство современных инструментов сочетают обе идеи: CPython компилирует твой файл в байт-код, а затем интерпретирует его, а JVM и движок JavaScript V8 компилируют часто используемый код в машинный прямо во время работы программы. Полная таблица сравнения есть на странице компилятор и интерпретатор.

Распространённые заблуждения

  • «Если компилируется, значит работает». Компилятор проверяет, что программа допустима, а не что она правильна. Логические ошибки и сбои во время выполнения, например деление на ноль, проходят сквозь него.
  • «Python не компилируется». CPython компилирует каждый файл в байт-код перед запуском. Файлы .pyc в папках __pycache__ и есть этот байт-код, сохранённый для модулей, которые импортирует программа, чтобы при следующем запуске пропустить этот шаг.
  • «Онлайн-компилятор является компилятором». Сайты, которые запускают твой код в браузере, отправляют его на сервер, где работает весь набор инструментов (компилятор, компоновщик и сама программа), и возвращают вывод. Компилятор только одна из этих частей.
  • «Скомпилированный код работает где угодно». Он работает на том процессоре и в той операционной системе, для которых собран. Переносимость дают повторная компиляция или виртуальная машина вроде JVM.

Что изучить дальше

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

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

Из каких частей состоит компилятор?
Компилятор обычно описывают как три части. Фронтенд читает исходный код, разбивает его на токены, строит синтаксическое дерево и проверяет типы. Средняя часть (middle end) оптимизирует промежуточное представление программы, а бэкенд генерирует машинный код или байт-код для целевого процессора.
Как компилятор может скомпилировать сам себя?
С помощью раскрутки (bootstrapping). Первую версию компилятора пишут на уже существующем языке или компилируют более старым компилятором, а затем с её помощью компилируют следующую версию, написанную уже на новом языке. Например, GCC написан в основном на C и C++ и собирается существующим компилятором C++, часто более ранней версией GCC.
Чем компилятор отличается от ассемблера?
Компилятор переводит язык высокого уровня вроде C или Rust, где одна строка может превратиться во множество машинных инструкций. Ассемблер переводит язык ассемблера, где каждая строка почти один к одному соответствует одной машинной инструкции. Компиляторы вроде GCC часто сначала создают код на ассемблере и передают его ассемблеру.
Что такое JIT-компилятор?
JIT-компилятор (just-in-time, «точно в срок») переводит код в машинный код во время работы программы, а не до неё. Он следит, какие части выполняются чаще всего, и компилирует именно их, поэтому долго работающая программа со временем ускоряется. JIT-компиляцию используют JVM, движок JavaScript V8 и PyPy.
Нужно ли устанавливать компилятор, чтобы учиться программировать?
Поначалу нет. Редакторы в браузере и онлайн-песочницы запускают компилятор или интерпретатор за тебя на сервере. Когда перейдёшь на свой компьютер, для C и C++ понадобится компилятор вроде GCC или Clang, для Java нужен JDK, а для Python интерпретатор Python.
Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ