EngineerSpock

Ликбез программиста: как работают трансляторы, компиляторы и интерпретаторы

Бинарный салют, друзья! На связи, как всегда, инженер Спок. Вы когда-нибудь задумывались, почему у программистов самые высокие зарплаты?
 

Бинарный салют, друзья! На связи, как всегда, инженер Спок.

Вы когда-нибудь задумывались, почему у программистов самые высокие зарплаты? Казалось бы, что проще – выучи любой язык программирования (явно не сложнее иностранного), и набирай себе на клавиатуре команды, печатай бабки. Только вот в отличие от любого иностранца, компьютер не понимает человеческий язык. В его алфавите только 2 символа – 0 и 1.

Сегодня поговорим о трансляторах – программах, которые переводят понятные человеку слова в понятные компьютеру двоичные коды. Нужно ли знать принципы работы этих программ, их виды, преимущества и ограничения? Если хотите остаться манки-кодером, впадающим в ступор при необходимости погружения во внутренности того или иного языка – тогда не нужно.

А если планируете достичь определенных успехов в этой увлекательной и очень перспективной области – тогда вперед! Я постараюсь простым и понятным языком рассказать о компиляторах, интерпретаторах и их гибридах, чтобы эти понятия не вызывали у вас дрожь.

Подписывайтесь, ставьте лайк. Да, вот прямо сейчас. Подписались и поставили?

Тогда поехали!

Лайкосы / Подписки / Курсы

На каком языке говорит ваш компьютер?

Итак, что же такое транслятор? Транслятор – переводчик. Строго говоря, и человека-переводчика с русского на китайский можно назвать транслятором. Люди могут переводить:

  • синхронно с оригиналом, по одной фразе, как только она произнесена;
  • последовательно, когда сначала выслушивается (и при необходимости фиксируется) фрагмент текста или весь текст и переводится уже после его произнесения через паузу, но целиком.

В ИТ транслятором может быть программа или устройство. Его цель – перевести программу на исходном языке (программирования или, скажем, языка разметки HTML) в целевой язык процессора или программы-исполнителя.

Сравните:

Вот исходный код на языке С, который пишет человек:
#include<.h>
int main() {
printf(“Hello, world!\n”);
return 0;

}

Достаточно понятные фразы, за исключением спецсимволов.

А вот его представление на целевом языке (в данном случае – ассемблере)

00000000004004f0<>:

4004f0:  55             push %rbp
4004f1:  48 89 e5         mov %rsp,%rbp
4004f4:  bf 00 06 40 00    mov $0x400600,%edi
4004f9:  b8 00 00 00 00    mov $0x0,%eax
4004fe:  e8 b3 fe ff ff       callq 4003b6
400503:  b8 00 00 00 00    mov $0x0,%eax
400508:  5d              pop %rbp

400509:  c3             retq

Смогли бы такое написать? Может и смогли бы, но только после литры выпитой. Хотя нет, тогда бы точно не смогли.

Процесс перевода сразу всей исходной программы для исполнения процессором называется компиляциейЕе результат – или машинный код (1 и 0) или байт-код (промежуточное представление исходной информации на языке низкого уровня, обычно похожим на ассемблер). В этом плане компиляция похожа на последовательный «человеческий» перевод.

В процессе перевода вся исходная программа загружается в память, где происходит проверка лексики, синтаксиса и семантики исходного кода. Т.е., компилятор проверяет корректность кода:

  • корректные ли ключевые слова написаны (проверка лексики) в корректной ли последовательности они следуют (проверка синтаксиса)
  • и корректны ли программные выражения по своему смыслу (проверка семантики).

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

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

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

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

Какие бывают компиляторы?

Выделяют два основных вида компиляторов: однопроходные и многопроходные.

Однопроходные обрабатывают исходный код программы за один проход и в основном используются для компиляции программ на языках с более простой и жесткой структурой.

Это – Pascal, ранние версии С, FORTRAN.

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

Несколько ходов выполняют компиляторы языков С#, SWIFT, Java.

Кроме однопроходных и многопроходных существуют еще:

  • кросс-компиляторы – это специальный вид компиляторов, которые позволяют программисту перекомпилировать код, написанный для одной платформы в код для другой платформы.

Например, приложение, изначально написанное на C/C++, можно с помощью Android Native Development Kit перевести в нативное приложение для платформы Android. Это даёт возможность кросс-компиляции для архитектур ARM, x86 и других, поддерживаемых Android –устройствами;

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

Примерами таких компиляторов являются:

  1. GCC (GNU Compiler Collection) с открытым исходным кодом, который поддерживает множество языков программирования на разных платформах (такие как C, C++, Objective-C, Fortran, Ada, и т.д.). Он используется во многих проектах с открытым исходным кодом, включая Linux, для компиляции приложений и драйверов.
  2. Clang для таких языков, как C, C++, Objective-C, и Swift. Он используется для компиляции приложений на macOS, iOS и других платформах.

Какие достоинства у программы, написанной на компилируемом языке?

  1. Она выполняется с высокой скоростью, т.к.имеет оптимизированный код, и самое быстрое устройство компьютера в качестве исполнителя – процессор.
  2. Такую программу трудно взломать, ведь исходный текст недоступен, а машинный код сложнее поддается взлому.
  3. Исполняемый файл можно запускать на платформе без участия компилятора неограниченное число раз.

Какие имеются ограничения у компилируемых программ?

  1. Компиляция, особенно многопроходная – длительный процесс и время на разработку программного обеспечения может увеличиваться. Тут зависит от того о каком конкретно языке речь и об архитектуре компилируемой программы. Компиляторы современных языков типа С# работают очень быстро, а вот с С++ могут быть проблемы
  2. .Сложная отладка: при любом изменении исходного текста необходимо компилировать программу заново. Эта проблема актуально в случае, если актуальна первая
  3. .Компилируемые программы в некоторых случаях платформозависимые. При переносе на другую платформу необходимо выполнить компиляцию под новую архитектуру.

Обо всех выделенных недостатках необходимо сказать, что всё зависит от конкретного языка и компилятора.

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

Резюме

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

Языки программирования с компиляторами – C, C++, Java, C#

Знакомьтесь -интерпретаторы

Совсем не так работает другой вид транслятора – интерпретатор. Он больше похож на переводчика-синхрониста. Интерпретатор загружает в память ОДНУ команду из программы, проводит ее лексический, синтаксический и семантический анализ и в случае отсутствия ошибок сразу же исполняет ее. И переходит к следующей. Это –«чистая интерпретация», без образования машинно-ориентированного кода. Она еще осталась в школьном Basic, языках простой структуры – языков сценариев, ЛИСП.

В современных языках, таких, как PERL, PHP, Python, Ruby, JavaScript применяется смешанная интерпретация, с созданием промежуточного кода.

Например, программа на питоне сначала компилируется в промежуточный байт-код, а затем интерпретируется виртуальной машиной, специально созданной для выполнения промежуточного байт-кода.

Интерпретируемые программы обладают преимуществами:

  • предоставляют возможность работы в интерактивном режиме, когда разработчик вводит код и может сразу же видеть результат;
  • быстрая и простая отладка в реальном времени, т.е. значительно сокращается время на разработку и улучшается качество конечного программного продукта;
  • они являются кросс-платформенными, так как могут работать на разных платформах, где установлен интерпретатор.

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

У интерпретаторов есть и недостатки.

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

Компиляторы и интерпретаторы тесно связаны между собой, и иногда можно заменить один на другой.

Например, компиляторы Java переводят исходный код Java в байт-код, который является абстрактной формой ассемблера. Некоторые реализации виртуальной машины Java работают как интерпретаторы, выполняющие по одной инструкции за раз. Другие работают, транслируя байт-код в локальный машинный код, а затем запуская его целиком.

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

Гибридная трансляция

Сегодня очень популярна гибридная трансляция, которая может быть реализована очень разными способами. Мы уже упоминали о том, что питоновский код сначала компилируется, а затем интерпретируется. Хотя, во многих источниках и пишется, что питоновский код интерпретируется в байт-код. Однако сам процесс по реализации всё же ближе к компиляции. Короче говоря, это в любом случае некий гибрид. Это не чистая прямолинейная интерпретация. Мы также упоминали виртуальные машины, которые исполняют байт-код.

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

Например, в C# и Java виртуальная машина осуществляет, так называемую JIT или just-in-time компиляцию во время исполнения, преобразуя байт-код в машинный налету. Это позволяет на ещё одном этапе навешивать различные оптимизации, например, благодаря тому, что виртуальная машина может собирать и анализировать статистику по исполнению одного и того же куска кода и преобразовывать его наиболее эффективно в зависимости от результатов.

Надо сказать, что сам по себе язык программирования это всегда просто спецификация. Сам по себе язык программирования не может быть интерпретируемым или компилируемым. Это зависит почти исключительно от того, как решили исполнять код на этом язык программирования. Структура самого языка оказывает дополнительное влияние на решение о том каким образом код будет исполняться: легче ли будет компилировать или интерпретироваться. Однако, ещё раз повторю, структура языка не является единственным фактором. Поэтому есть разные реализации питона, есть CPython, который не предполагает JIT-компиляции, а есть PyPi, которые предполагает. Едем дальше.

Транспиляторы – что это такое и зачем они нужны?

Мы не рассмотрели еще один вид компиляторов – транспиляторы, транспайлеры (transpiler). Они становятся все более востребованными, особенно при написании кроссплатформенного кода, который можно использовать на различных устройствах и платформах.

Их функция – переводить текст программы с одного языка высокого уровня на другой язык высокого уровня. Этим они отличаются от прямых компиляторов, которые переводят исходный текст напрямую в машинный код.

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

Например, Babel —JavaScript транспилятор, позволяющий использовать новейшие функции языка на базе старых версий браузеров.

Транспилятор поможет при переводе с одного языка программирования на другой.

Например, браузеры могут понимать только HTML, CSS и Javascript. Но мы хотим использовать функции, которых нет в JS, но есть в языках Typescript (TS), CoffeeScript или ClojureScript. Транспиляторы преобразуют программы на этих языках в Javascript. При этом вероятность синтаксических ошибок будет исключена, а браузеры без проблем прочитают этот переделанный код.

Резюме

Подытожить эту статью не так-то просто.

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

Зачастую стало не так-то просто отличить какой язык является интерпретируемым, а какой компилируемым. Напомню, что это лишь форма речи, на самом деле языки сами по себе не являются интерпретируемыми или компилируемыми.

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

В 99% случае тормозная программа это результат криворукости программистов, а не вина языка программирования или его компилятора.

Конкретный язык для решения конкретной задачи вообще подбирается довольно редко, такое происходит чаще всего для решения очень специфических задач.

Например, для разработки программы космического шаттла вряд ли будет выбран питон или джава скрипт.

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

Надеюсь статья была для вас полезна. А я как всегда напоминаю, что все, что я выпускаю на канале я делаю только лишь с целью – на вас заработать. Поэтому идите по ссылке в начале статьи и покупайте профессию пайтон разработчика, которая состоит из целого набора крутых курсов, а стоит сущие копейки. До скорого.

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *