Что это - компилятор, и как он работает. Как устроен компилятор

Одной из ключевых характеристик PHP является то, что это интерпретируемый язык программирования. С другой стороны, языки программирования наподобие C , изначально разрабатывались для компиляции. Что это значит?

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

Рабочий цикл программы

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

Когда пишете программу, вы хотите, чтобы ее инструкции работали на компьютере. Компьютер обрабатывает информацию с помощью процессора, который поэтапно выполняет инструкции, закодированные в двоичном формате. Как из выражения «a = 3; » получить закодированные инструкции, которые процессор может понять?

Мы делаем это с помощью компиляции. Существует специальные приложения, известные как компиляторы. Они принимают программу, которую вы написали. Затем анализируют и разбирают каждую часть программы и строят машинный код для процессора. Часто его также называют объектным кодом.

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

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

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

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

Не все языки программирования учитывают это в своей концепции. Например, Java предназначался для запуска в «интерпретирующей » среде, а Python всегда должен интерпретироваться.

Интерпретация программы

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

Интерпретатор — это исполняемый файл, который поэтапно читает программу, а затем обрабатывает, сразу выполняя ее инструкции.

Другими словами, интерпретатор выполняет программу поэтапно как часть собственного исполняемого файла. Объектный код не передается процессору, интерпретатор сам является объектным кодом, построенным таким образом, чтобы его можно было вызвать в определенное время.

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


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

Например, так работают такие языки программирования, как Python . Вы пишете программу. Затем вводите код в интерпретатор Python , и он выполняет все описанные вами шаги. В командной строке вы можете ввести примерно следующее:

C:>python myprogram.py

В этой команде Python — это исполняемый файл. Вы вводите в него все, что находится в файле myprogram.py, и он выполняет эти инструкции. Компьютер не запустит myprogram.py без Python . Это не машинный код, который понимает процессор. Можно скомпилировать программы Python в объектный или машинный код и запустить его непосредственно в процессоре. Но эта процедура включает в себя компиляцию кода и добавление в качестве ее части всего интерпретатора Python .

Природа интерпретатора

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

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

Такой код быстрее обрабатывается, и его проще написать для исполнителя (части интерпретатора, которая исполняет ), который считывает байтовый код, а не код источника.

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

У меня есть эмулятор для игровой приставки NIntendo . Когда я загружаю ROM-файл Dragon Warrior , он форматируется в машинный код, который понимает только процессор NES . Но если я создаю виртуальный процессор, который интерпретирует байтовый код во время работы на другом процессоре, я могу запустить Dragon Warrior на любой машине с эмулятором.

Это использует концепция компиляции Java , а также все интерпретаторы. На любом процессоре, для которого я могу создать интерпретатор / эмулятор, можно запускать мои интерпретируемые программы / байтовый код. В этом заключается основное преимущество интерпретатора над компилятором.

За и против

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

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

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

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

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

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

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

Это проблема для конкретных real-time приложений, таких как игры с высоким разрешением и симуляцией. Некоторые интерпретаторы содержат компоненты, которые называются just-in-time компиляторами (JIT ). Они компилируют программу непосредственно перед ее исполнением. Это специальные программы, вынесенные за рамки интерпретатора. Но поскольку процессоры становятся все более мощными, данная проблема становится менее актуальной.

Заключение

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

Для меня не имеет значения, скомпилировано что-то или интерпретировано, если оно может выполнить задачу эффективно.

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

Компьютеры сами по себе способны выполнять только очень ограниченный набор операций, называемых машинными кодами. В старые времена, когда появились первые компьютеры, программы писались в машинных кодах, представляющих собой последовательности двоичных чисел, однозначно воспринимаемых компьютером. В конце 50-х кодов прошлого века появились первые языки программирования, такие как язык ассемблера и Фортран. Для того, чтобы компьютер мог понять программу, написанную на каком-то языке программирования, необходим переводчик ( транслятор ) такой программы в машинные коды. Отметим, что, если оператор языка ассемблера отображается при трансляции чаще всего 1Некоторые операторы языка ассемблера, например, такие, как операторы ввода/вывода, отображаются в несколько машинных команд. в одну машинную инструкцию, предложения языков более высокого уровня отображаются, вообще говоря, в несколько машинных инструкций.

Трансляторы бывают двух типов: компиляторы ( compiler ) и интерпретаторы ( interpreter ). Процесс компиляции состоит из двух частей: анализа ( analysis ) и синтеза ( synthesis ). Анализирующая часть компилятора разбивает исходную программу на составляющие ее элементы (конструкции языка) и создает промежуточное представление исходной программы. Синтезирующая часть из промежуточного представления создает новую программу, которую компьютер в состоянии понять. Такая программа называется объектной программой. Объектная программа может в дальнейшем выполняться без перетрансляции. В качестве промежуточного представления обычно используются деревья, в частности, так называемые деревья разбора. Под деревом разбора понимается дерево , каждый узел которого соответствует некоторой операции , а сыновья этого узла - операндам.

Интерпретатор

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

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

В том случае, если исходный язык достаточно прост (например, если это язык ассемблера или Basic ), то никакое промежуточное представление не нужно, и тогда интерпретатор - это простой цикл . Он выбирает очередную инструкцию языка из входного потока, анализирует и выполняет ее. Затем выбирается следующая инструкция . Этот процесс продолжается до тех пор, пока не будут выполнены все инструкции, либо пока не встретится инструкция , означающая окончание процесса интерпретации.


Компилятор


Компилятор переводит программы с одного языка на другой. Входом компилятора служит цепочка символов , составляющая исходную программу на языке программирования . Выход компилятора (объектная программа ) также представляет собой цепочку символов, но принадлежащую другому языку , например, языку некоторого компьютера. При этом сам компилятор написан на языке , возможно, отличающемся от первых двух. Будем называть язык исходным языком, язык - целевым языком, а язык - языком реализации. Таким образом, можно говорить о компиляторе как об Pascal и кончая современными объектно-ориентированными языками такими, как C# и Java . Практически каждый язык программирования имеет какие-то особенности с точки зрения создателя транслятора. Однако мы начнем с рассмотрения разнообразных целевых языков компиляторов.

Компиля́тор - программа или техническое средство, выполняющее компиляцию .

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

Компилировать - проводить трансляцию машинной программы с проблемно-ориентированного языка на машинно-ориентированный язык.

Виды компиляторов

    Векторизующий . Транслирует исходный код в машинный код компьютеров, оснащённых векторным процессором.

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

    Диалоговый . См.: диалоговый транслятор.

    Инкрементальный . Повторно транслирует фрагменты программы и дополнения к ней без перекомпиляции всей программы.

    Интерпретирующий (пошаговый) . Последовательно выполняет независимую компиляцию каждого отдельного оператора (команды) исходной программы.

    Компилятор компиляторов . Транслятор, воспринимающий формальное описание языка программирования и генерирующий компилятор для этого языка.

    Отладочный . Устраняет отдельные виды синтаксических ошибок.

    Резидентный . Постоянно находится в оперативной памяти и доступен для повторного использования многими задачами.

    Самокомпилируемый . Написан на том же языке, с которого осуществляется трансляция.

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

Виды компиляции

    Пакетная . Компиляция нескольких исходных модулей в одном пункте задания.

    Построчная . То же, что и интерпретация.

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

Структура компилятора

Процесс компиляции состоит из следующих этапов:

    Лексический анализ. На этом этапе последовательность символов исходного файла преобразуется в последовательность лексем.

    Синтаксический (грамматический) анализ. Последовательность лексем преобразуется в дерево разбора.

    Семантический анализ. Дерево разбора обрабатывается с целью установления его семантики (смысла) - например, привязка идентификаторов к их декларациям, типам, проверка совместимости, определение типов выражений и т. д. Результат обычно называется «промежуточным представлением/кодом», и может быть дополненным деревом разбора, новым деревом, абстрактным набором команд или чем-то ещё, удобным для дальнейшей обработки.

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

    Генерация кода. Из промежуточного представления порождается код на целевом языке.

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

Генерация кода

Генерация машинного кода

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

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

Для каждой целевой машины (IBM, Apple, Sun и т. д.) и каждой операционной системы или семейства операционных систем, работающих на целевой машине, требуется написание своего компилятора. Существуют также так называемые кросс-компиляторы , позволяющие на одной машине и в среде одной ОС генерировать код, предназначенный для выполнения на другой целевой машине и/или в среде другой ОС. Кроме того, компиляторы могут оптимизировать код под разные модели из одного семейства процессоров (путём поддержки специфичных для этих моделей особенностей или расширений наборов инструкций). Например, код, скомпилированный под процессоры семейства Pentium, может учитывать особенности распараллеливания инструкций и использовать их специфичные расширения - MMX, SSE и т. п.

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

Генерация байт-кода

Результатом работы компилятора может быть программа на специально созданном низкоуровневом языке, подлежащем интерпретации виртуальной машиной . Такой язык называется псевдокодом или байт-кодом. Как правило, он не является машинным кодом какого-либо компьютера и программы на нём могут исполняться на различных архитектурах, где имеется соответствующая виртуальная машина, но в некоторых случаях создаются аппаратные платформы, напрямую поддерживающие псевдокод какого-либо языка. Например, псевдокод языка Java называется байт-кодом Java (англ. Java bytecode ) и выполняется в Java Virtual Machine, для его прямого исполнения была создана спецификация процессора picoJava. Для платформы.NET Framework псевдокод называется Common Intermediate Language (CIL), а среда исполнения - Common Language Runtime (CLR).

Некоторые реализации интерпретируемых языков высокого уровня (например, Perl) используют байт-код для оптимизации исполнения: затратные этапы синтаксического анализа и преобразование текста программы в байт-код выполняются один раз при загрузке, затем соответствующий код может многократно использоваться без промежуточных этапов.

Динамическая компиляция

Основная статья: JIT-компиляция

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

CIL-код также компилируется в код целевой машины JIT-компилятором, а библиотеки.NET Framework компилируются заранее.

Декомпиляция

Существуют программы, которые решают обратную задачу - перевод программы с низкоуровневого языка на высокоуровневый. Этот процесс называют декомпиляцией, а такие программы - декомпиляторами. Но поскольку компиляция - это процесс с потерями, точно восстановить исходный код, скажем, на C++, в общем случае невозможно. Более эффективно декомпилируются программы в байт-кодах - например, существует довольно надёжный декомпилятор для Flash. Разновидностью декомпилирования являетсядизассемблирование машинного кода в код на языке ассемблера, который почти всегда выполняется успешно (при этом сложность может представлять самомодифицирующийся код или код, в котором собственно код и данные не разделены). Связано это с тем, что между кодами машинных команд и командами ассемблера имеется практически взаимно-однозначное соответствие.

Раздельная компиляция

Раздельная компиляция (англ. separate compilation ) - трансляция частей программы по отдельности с последующим объединением их компоновщиком в единый загрузочный модуль.

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

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

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

Компили́ровать - проводить трансляцию машинной программы с предметно-ориентированного языка на машинно-ориентированный язык. .

Виды компиляторов [ | ]

  • Векторизующий . Базируется на трансляторе, транслирующем исходный код в машинный код компьютеров, оснащённых векторным процессором .
  • Гибкий . Сконструирован по модульному принципу, управляется таблицами и запрограммирован на языке высокого уровня или реализован с помощью.
  • Диалоговый . См.: .
  • Инкрементальный . Пересобирает программу, заново транслируя только измененные фрагменты программы без перетрансляции всей программы.
  • Интерпретирующий (пошаговый) . Последовательно выполняет независимую компиляцию каждого отдельного оператора (команды) исходной программы.
  • Компилятор компиляторов . Транслятор, воспринимающий формальное описание языка программирования и генерирующий компилятор для этого языка.
  • Отладочный . Устраняет отдельные виды синтаксических ошибок .
  • Резидентный . Постоянно находится в оперативной памяти и доступен для повторного использования многими задачами.
  • Самокомпилируемый . Написан на том же языке программирования, с которого осуществляется трансляция.
  • Универсальный . Основан на формальном описании синтаксиса и семантики входного языка. Составными частями такого компилятора являются: ядро, синтаксический и семантический загрузчики.

Виды компиляции [ | ]

Структура компилятора [ | ]

Процесс компиляции состоит из следующих этапов:

В первом случае компилятор представляет собой пакет программ, включающий в себя трансляторы с разных языков программирования и компоновщики. Такой компилятор может компилировать программу, разные части исходного текста которой написаны на разных языках программирования. Нередко такие компиляторы управляются встроенным интерпретатором того или иного командного языка. Яркий пример таких компиляторов - имеющийся во всех UNIX-системах (в частности в Linux) компилятор make .

Во втором случае компилятор де-факто выполняет только трансляцию и далее вызывает компоновщик как внешнюю подпрограмму, который и компонует машинно-ориентированную программу. Этот факт нередко служит поводом считать компилятор разновидностью транслятора, что естественно неверно, - все современные компиляторы такого типа поддерживают организацию импорта программой процедуры (функции) из уже оттранслированого программного модуля, написанного на другом языке программирования. Так в программу на С/С++ можно импортировать функцию написанную например Pascal или Fortran . Аналогично и напротив написанная на С/С++ функция может быть импортирована в Pascal- или Fortran-программу соответственно. Это как правило было бы невозможно без поддержки многими современными компиляторами организации обработки входных данных в процедуру (функций) в соответствии с соглашениями других языков программирования. Например современные компиляторы с языка Pascal помимо соглашения самого Pascal поддерживает организацию обработки процедурая/функцией входных в соответствии с соглашениями языка С/С++. (Чтобы на уровне машинного кода написанная на Pascal процедура/функция работала с входными параметрами в соответствии с соглашениями языка С/С++, - оператор объявления такой Pascal-процедуры/Pascal-функции должен содержать ключевое слово cdecl .) Примерами таких компиляторов являются компиляторы со всех без исключения языков программирования, используемые непосредственно.

Трансляция программы как неотъемлемая составляющая компиляции включает в себя:

Генерация кода [ | ]

Генерация машинного кода [ | ]

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

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

Некоторые компиляторы переводят программу с языка высокого уровня не прямо в машинный код, а на язык ассемблера . (Пример: PureBasic , транслирующий бейсик-код в ассемблер FASM .) Это делается для упрощения части компилятора, отвечающей за генерацию кода, и повышения его переносимости (задача окончательной генерации кода и привязки его к требуемой целевой платформе перекладывается на ассемблер), либо для возможности контроля и исправления результата компиляции (в т. ч. ручной оптимизации) программистом.

Генерация байт-кода [ | ]

Результатом работы компилятора может быть программа на специально созданном низкоуровневом языке двоично-кодовых команд, выполняемых виртуальной машиной . Такой язык называется псевдокодом или байт-кодом . Как правило, он не есть машинный код какого-либо компьютера и программы на нём могут исполняться на различных архитектурах, где имеется соответствующая виртуальная машина, но в некоторых случаях создаются аппаратные платформы, напрямую выполняющие псевдокод какого-либо языка. Например, псевдокод языка Java называется байт-кодом Java и выполняется в Java Virtual Machine , для его прямого исполнения была создана спецификация процессора picoJava . Для платформы .NET Framework псевдокод называется Common Intermediate Language (CIL), а среда исполнения - Common Language Runtime (CLR).

Некоторые реализации интерпретируемых языков высокого уровня (например, Perl) используют байт-код для оптимизации исполнения: затратные этапы синтаксического анализа и преобразование текста программы в байт-код выполняются один раз при загрузке, затем соответствующий код может многократно использоваться без перекомляции.

Динамическая компиляция [ | ]

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

Наиболее популярной разновидностью динамической компиляции является JIT . Другой разновидностью является .

CIL-код также компилируется в код целевой машины JIT-компилятором, а библиотеки .NET Framework компилируются заранее.

Трансляция байт-кода в машинный код [ | ]

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

Декомпиляция [ | ]

Существуют программы, которые решают обратную задачу - перевод программы с низкоуровневого языка на высокоуровневый. Этот процесс называют декомпиляцией, а такие программы - декомпиляторами . Но поскольку компиляция - это процесс с потерями, точно восстановить исходный код, скажем, на C++, в общем случае невозможно. Более эффективно декомпилируются программы в байт-кодах - например, существует довольно надёжный декомпилятор для Flash . Разновидностью декомпиляции является дизассемблирование машинного кода в код на языке ассемблера, который почти всегда благополучно выполняется (при этом сложность может представлять самомодифицирующийся код или код, в котором собственно код и данные не разделены). Связано это с тем, что между кодами машинных команд и командами ассемблера имеется практически взаимно-однозначное соответствие.

Раздельная компиляция [ | ]

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

Появление раздельной компиляции и выделение компоновки как отдельной стадии произошло значительно позже создания компиляторов. В связи с этим вместо термина «компилятор» иногда используют термин «транслятор» как его синоним: либо в старой литературе, либо когда хотят подчеркнуть его способность переводить программу в машинный код (и наоборот, используют термин «компилятор» для подчёркивания способности собирать из многих файлов один). Вот только использование в таком контексте терминов «компилятор» и «транслятор» неправильно. Даже если компилятор выполняет трансляцию программы самостоятельно, поручая компоновку вызываемой внешней программе-компоновщику, такой компилятор не может считаться разновидностью транслятора, - транслятор выполняет трансляцию исходной программы и только. И уж тем более не являются трансляторами компиляторы вроде системной утилиты-компилятора make , имеющейся во всех UNIX-системах.

Если вы только начинаете изучать программирование или же просто интересовались данным вопросом, наверняка вам на глаза попадалось таинственное слово «компилятор». Это с виду страшное понятие способно отпугнуть людей. В данной статье постараемся разобраться, так ли все ужасно. Что такое компилятор?

Определение и история появления

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

Появились такие программы вместе с зарождением первых языков программирования. Произошло это в конце 50-х годов. Получается, что история, которая связана с компиляторами и языками программирования насчитывает уже более шести десятков лет. Несмотря на такой серьезный срок, данное направление компьютерной науки ни в коем случае нельзя назвать устаревшим или устоявшимся. Наоборот, с ходом времени, с появлением новых отраслей и задач, для решения которых применяются компьютеры, возникает потребность в разработке новых, более удобных языков программирования. Соответственно, для этих языков нужны компиляторы. Windows, Linux, MacOS - для каждой платформы существуют свои разработки.

Принцип работы

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

Просто, но сложно

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

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

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

Трудности в создании

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

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

Класс и компилятор

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

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

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

Разработка компиляторов как работа

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

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

Таким образом, теперь вам должно быть понятно, что такое компилятор. Это программа-переводчик между разработчиком и компьютером, без которой в сфере компьютерной науки никуда.



Просмотров