Что такое компилятор?
Содержание:
- Раздельная компиляция
- Генерация кода[ | код]
- Основные функционал и терминология[ | код]
- Какие ошибки может определить компилятор?
- Кросс-курс 160 Рублей (Россия) к другим валютам
- Метакомпиляторы
- Рекомендации по применению формата
- Структура компилятора[ | код]
- Пользователям Code::Blocks
- Трансляторы
- Структура компилятора
- Примечания
- Генерация кода
- Указание целевых входных и выходных параметров
- Интерпретация программы
Раздельная компиляция
Раздельная компиляция (англ. separate compilation) — трансляция частей программы по отдельности с последующим объединением их компоновщиком в единый загрузочный модуль.
Исторически особенностью компилятора, отражённой в его названии (англ. compile — собирать вместе, составлять), являлось то, что он производил как трансляцию, так и компоновку, при этом компилятор мог порождать сразу машинный код. Однако позже, с ростом сложности и размера программ (и увеличением времени, затрачиваемого на перекомпиляцию), возникла необходимость разделять программы на части и выделять библиотеки, которые можно компилировать независимо друг от друга. В процессе трансляции программы сам компилятор или вызываемый компилятором транслятор порождает объектный модуль, содержащий дополнительную информацию, которая потом — в процессе компоновки частей в исполнимый модуль — используется для связывания и разрешения ссылок между частями программы. Раздельная компиляция также позволяет писать разные части исходного текста программы на разных языках программирования.
Появление раздельной компиляции и выделение компоновки как отдельной стадии произошло значительно позже создания компиляторов. В связи с этим вместо термина «компилятор» иногда используют термин «транслятор» как его синоним: либо в старой литературе, либо когда хотят подчеркнуть его способность переводить программу в машинный код (и наоборот, используют термин «компилятор» для подчёркивания способности собирать из многих файлов один). Вот только использование в таком контексте терминов «компилятор» и «транслятор» неправильно. Даже если компилятор выполняет трансляцию программы самостоятельно, поручая компоновку вызываемой внешней программе-компоновщику, такой компилятор не может считаться разновидностью транслятора, — транслятор выполняет трансляцию исходной программы и только. И уж тем более не являются трансляторами компиляторы вроде системной утилиты-компилятра make, имеющейся во всех UNIX-системах. Утилита
Собственно утилита make — яркий пример довольно удачной реализации раздельной компиляции. Работа утилиты make управляется сценарием на интерпретируемым утилитой входном языке, известном как makefile, содержащемся в задаваемом при запуске утилиты входном текстовом файле. Сама утилита не выполняет ни трансляцию ни компоновку, — де-факто утилита make функционирует как диспетчер процесса компиляции, организующий компиляцию программы в соответствии с заданным сценарием. В частности в ходе компиляции целевой программы утилита make вызывает трансляторы с языков программирования транслирующие разные части исходной программы в объектный код, и уже после этого вызывается тот или иной компоновщик, компонующий конечный исполняемый программный или библиотечный программный модуль. При этом разные части программы, оформляемые в виде отдельных файлов исходно текста, могут быть написаны как на одном языке программирования так и на разных языках программирования. В процессе перекомпиляции программы транслируются только измененные части-файлы исходного текста программы, в следствие чего длительность перекомпиляции программы значительно (порой на порядок) сокращается.
Генерация кода[ | код]
Генерация машинного кода | код
Большинство компиляторов переводит программу с некоторого высокоуровневого языка программирования в машинный код, который может быть непосредственно выполнен физическим процессором. Как правило, этот код также ориентирован на исполнение в среде конкретной операционной системы, поскольку использует предоставляемые ею возможности (системные вызовы, библиотеки функций). Архитектура (набор программно-аппаратных средств), для которой компилируется (собирается) машинно-ориентированная программа, называется целевой машиной.
Результат компиляции — исполнимый программный модуль — обладает максимально возможной производительностью, однако привязан к конкретной операционной системе (семейству или подсемейству ОС) и процессору (семейству процессоров) и не будет работать на других.
Для каждой целевой машины (IBM, Apple, Sun, Эльбрус и т. д.) и каждой операционной системы или семейства операционных систем, работающих на целевой машине, требуется написание своего компилятора. Существуют также так называемые кросс-компиляторы, позволяющие на одной машине и в среде одной ОС генерировать код, предназначенный для выполнения на другой целевой машине и/или в среде другой ОС. Кроме того, компиляторы могут оптимизировать код под разные модели из одного семейства процессоров (путём поддержки специфичных для этих моделей особенностей или расширений наборов команд). Например, код, скомпилированный под процессоры семейства Pentium, может учитывать особенности распараллеливания инструкций и использовать их специфичные расширения — MMX, SSE и т. п.
Некоторые компиляторы переводят программу с языка высокого уровня не прямо в машинный код, а на язык ассемблера. (Пример: PureBasic, транслирующий бейсик-код в ассемблер FASM.) Это делается для упрощения части компилятора, отвечающей за генерацию кода, и повышения его переносимости (задача окончательной генерации кода и привязки его к требуемой целевой платформе перекладывается на ассемблер), либо для возможности контроля и исправления результата компиляции (в том числе ручной оптимизации) программистом.
Генерация байт-кода | код
Результатом работы компилятора может быть программа на специально созданном низкоуровневом языке двоично-кодовых команд, выполняемых виртуальной машиной. Такой язык называется псевдокодом или байт-кодом. Как правило, он не есть машинный код какого-либо компьютера и программы на нём могут исполняться на различных архитектурах, где имеется соответствующая виртуальная машина, но в некоторых случаях создаются аппаратные платформы, напрямую выполняющие псевдокод какого-либо языка. Например, псевдокод языка Java называется байт-кодом Java и выполняется в Java Virtual Machine, для его прямого исполнения была создана спецификация процессора picoJava. Для платформы .NET Framework псевдокод называется Common Intermediate Language (CIL), а среда исполнения — Common Language Runtime (CLR).
Некоторые реализации интерпретируемых языков высокого уровня (например, Perl) используют байт-код для оптимизации исполнения: затратные этапы синтаксического анализа и преобразование текста программы в байт-код выполняются один раз при загрузке, затем соответствующий код может многократно использоваться без перекомляции.
Динамическая компиляция | код
Основная статья: Динамическая компиляция (англ.)
Из-за необходимости интерпретации байт-код выполняется значительно медленнее машинного кода сравнимой функциональности, однако он более переносим (не зависит от операционной системы и модели процессора). Чтобы ускорить выполнение байт-кода, используется динамическая компиляция, когда виртуальная машина транслирует псевдокод в машинный код непосредственно перед его первым исполнением (и при повторных обращениях к коду исполняется уже скомпилированный вариант).
Наиболее популярной разновидностью динамической компиляции является JIT. Другой разновидностью является инкрементальная компиляция.
CIL-код также компилируется в код целевой машины JIT-компилятором, а библиотеки .NET Framework компилируются заранее.
Основные функционал и терминология[ | код]
Компиля́ция — сборка программы, включающая:
- трансляцию всех модулей программы, написанных на одном или нескольких исходных языках программирования высокого уровня и/или языке ассемблера, в эквивалентные программные модули на низкоуровневом языке, близком машинному коду (абсолютный код, объектный модуль, иногда на язык ассемблера) или непосредственно на машинном языке или ином двоичнокодовом низкоуровневом командном языке
- и последующую сборку исполняемой машинной программы, в том числе вставка в программу кода всех функций, импортируемых из статических библиотек и/или генерация кода запроса к ОС на загрузку динамических библиотек, из которых программой функции будут вызываться.
Если компилятор генерирует исполняемую машинную программу на машинном языке, то такая программа непосредственно исполняется физической программируемой машиной (например компьютером). В других случаях исполняемая машинная программа выполняется соответствующей виртуальной машиной.
Входная информация для компилятора есть:
- на фазе трансляции: исходный код программы, являющийся описанием алгоритма или программы на предметно-ориентированном языке программирования;
- на фазе компоновки: сгенерированные на фазе трансляции файлы объектных кодов модулей программы, а также файлы объектных кодов статических библиотек и данные о используемых динамических библиотеках.
На выходе компилятора — эквивалентное описание алгоритма на машинно-ориентированном языке (объектный код, байт-код).
Компили́ровать — проводить сборку машинной программы, включая:
- трансляцию с предметно-ориентированного языка на машинно-ориентированный язык,
- компоновка исполняемой машинно-ориентированной программы из сгенерированных на фазе трансляции объектных модулей — модулей, содержащих части кода программы на машинно-ориентированного кода программы.
Довольно часто компиляторы с языков высокого уровня выполняют лишь трансляцию исходного кода, компоновку же поручая внешнему компоновщику, — компоновщику, представляющему самостоятельную программу, вызываемую компилятором как внешняя подпрограмма. Вследствие этого компилятор многие считают разновидность транслятора, что неверно…
Какие ошибки может определить компилятор?
Когда компилятор анализирует текст программы, он проверяет, соответствует ли запись оператора стандартам языка. Если найдено несоответствие, то компилятор выводит об этом информацию пользователю в виде ошибки. Когда вся программа разобрана, пользователь видит список ошибок, которые есть в коде, и может их исправить.
Пока программист не исправит ошибки, компилятор не перейдет к следующему этапу — генерации машинного кода для процессора.
Чаще всего компилятор показывает пользователю:
- ошибки объявления переменных или отсутствие их начальных значений
- ошибки несоответствия типов
- ошибки неправильной записи операторов и функций
Иногда компилятор определяет код, который при выполнении дает неправильный результат. Но преобразовать такую программу в машинный код все-таки можно. В этом случае компилятор показывает пользователю предупреждение
Такая реакция компилятора больше похожа на рекомендации, но на них стоит обратить внимание. Программист сам решает оставить код с предупреждением или изменить программу.
Анализируя текст программы, компилятор не только ищет ошибки, но еще и упрощает ее код
Такой процесс называется оптимизацией.
Во время оптимизации компилятор изменяет программный код, но функции, которые выполняла программа, остаются прежними.
Кросс-курс 160 Рублей (Россия) к другим валютам
Метакомпиляторы
Метакомпиляторы отличаются от генераторов парсеров тем, что принимают на вход программу, написанную на . Их входные данные состоят из грамматического анализа формул и операций преобразования, которые создают абстрактные синтаксические деревья, или просто выводят отформатированные текстовые строки, которые могут быть стеками машинного кода.
Многие из них могут быть запрограммированы на их собственном метаязыке, что позволяет им компилировать себя, что делает их самодостаточными компиляторами расширяемых языков.
Многие метакомпиляторы на работе . Его компилятор META II , впервые выпущенный в 1964 году, был первым документированным метакомпилятором. Имея возможность определять свой собственный язык и другие, META II приняла с встроенным выводом (производство кода) . Он также переведен на один из самых ранних экземпляров виртуальной машины . Лексический анализ проводился встроенными функциями распознавания токенов: .ID, .STRING и .NUMBER. Строки в кавычках в синтаксической формуле распознают несохраняемые лексемы.
TREE-META , метакомпилятор Шорре второго поколения, появился примерно в 1968 году. Он расширил возможности META II, добавив нечеткие правила, отделяющие создание кода от анализа грамматики. Операции преобразования дерева в синтаксической формуле создают абстрактные синтаксические деревья , над которыми работают неанализируемые правила. Неразборчивое сопоставление с образцом дерева обеспечивало возможность оптимизации глазком .
, описанный в публикации ACM 1970 года, представляет собой метакомпилятор Шорре третьего поколения, который добавляет к грамматическому анализу правила лексирования и операторы поиска с возвратом. LISP 2 был связан с нечеткими правилами TREEMETA на языке генератора CWIC. Благодаря обработке LISP 2 CWIC может генерировать полностью оптимизированный код. CWIC также обеспечил генерацию двоичного кода в разделы именованного кода. Одно- и многопроходные компиляции могут быть реализованы с использованием CWIC.
CWIC скомпилирован в 8-битные инструкции машинного кода с адресацией байтов, в первую очередь предназначенные для создания кода IBM System / 360.
Более поздние поколения не документированы публично. Одной из важных функций может быть абстракция набора команд целевого процессора, генерирующая в псевдо-машинный набор команд макросы, которые могут быть отдельно определены или сопоставлены с инструкциями реальной машины. Оптимизация, применяемая к последовательным инструкциям, затем может быть применена к псевдоинструкции перед их расширением в целевой машинный код.
Рекомендации по применению формата
Структура компилятора[ | код]
Процесс компиляции состоит из следующих этапов:
- Трансляция программы — трансляция всех или только изменённых модулей исходной программы.
- компоновка машинно-ориентированной программы.
Структурные реализации компилятора могут быть следующими:
- И транслятор, и компоновщик могут целиком входит в состав компилятора как исполняемое программы.
- Компилятор сам выполняет лишь трансляцию компилируемой программы, компоновка же программы выполняется вызываемой компилятором отдельной программой-компоновщиком. Практически все современные компиляторы построены по такой схеме.
- Пакет программ, включающий в себя трансляторы с разных языков программирования и компоновщики.
По первой схеме строились самые первые компиляторы, — для современных компиляторов такая схема построения нехарактерна.
По второй схеме построены все без исключения компиляторы с языков высокого уровня. Любой такой компилятор сам выполняет только трансляцию и далее вызывает компоновщик как внешнюю подпрограмму, который и компонует машинно-ориентированную программу. Такая схема построения легко позволяет компилятору работать и в режиме транслятора с соответствующего языка программирования. Этот обстоятельство нередко служит поводом считать компилятор разновидностью транслятора, что естественно неверно, — все современные компиляторы такого типа все же выполняют компоновку, пусть и силами вызываемого компилятором внешнего компоновщика, тогда как транслятор сам никогда не выполняет вызов внешнего компоновщика. Но это же обстоятельство позволяет компилятору с одного языка программирования на фазе компоновки включать в программу написанную на одном языке программирования функции-подпрограммы из уже оттранслированных соответствующим транслятором/компилятором, написанные на ином языке программирования. Так в программу на С/С++ можно вставлять функции написанные например на Pascal или Fortran. Аналогично и напротив написанная на С/С++ функции могут быть вставлены в Pascal- или Fortran-программу соответственно. Это было бы невозможно без поддержки многими современными компиляторами генерации кода вызова процедур (функций) в соответствии с соглашениями иных языков программирования. Например современные компиляторы с языка Pascal помимо организации вызова процедур/функций в стандарте самого Pascal поддерживают организацию вызова процедурой/функцией в соответствии с соглашениями языка С/С++. (Например чтобы на уровне машинного кода написанная на Pascal процедура/функция работала с входными параметрами в соответствии с соглашениями языка С/С++, — оператор объявления такой Pascal-процедуры/Pascal-функции должен содержать ключевое слово cdecl.)
Наконец по третьей схеме построены компиляторы, представляющие собой целые системы, включающие в себя трансляторы с разных языков программирования и компоновщики. Также любой такой компилятор может использовать в качестве транслятора любой способный работать в режиме транслятора компилятор с конкретного языка высокого уровня. Естественно такой компилятор может компилировать программу, разные части исходного текста которой написаны на разных языках программирования. Нередко такие компиляторы управляются встроенным интерпретатором того или иного командного языка. Яркий пример таких компиляторов — имеющийся во всех UNIX-системах (в частности в Linux) компилятор make.
Трансляция программы как неотъемлемая составляющая компиляции включает в себя:
- Лексический анализ. На этом этапе последовательность символов исходного файла преобразуется в последовательность лексем.
- Синтаксический (грамматический) анализ. Последовательность лексем преобразуется в древо разбора.
- Семантический анализ. На этой фазе древо разбора обрабатывается с целью установления его семантики (смысла) — например, привязка идентификаторов к их объявлениям, типам данных, проверка совместимости, определение типов выражений и т. д. Результат обычно называется «промежуточным представлением/кодом», и может быть дополненным древом разбора, новым деревом, абстрактным набором команд или чем-то ещё, удобным для дальнейшей обработки.
- Оптимизация. Выполняется удаление излишних конструкций и упрощение кода с сохранением его смысла. Оптимизация может быть на разных уровнях и этапах — например, над промежуточным кодом или над конечным машинным кодом.
- Генерация кода. Из промежуточного представления порождается код на целевом машинно-ориентированном языке.
Пользователям Code::Blocks
Чтобы создать новый проект, запустите Code::Blocks, выберите :
Затем появится диалоговое окно, где вам нужно будет выбрать и нажать :
Затем выберите язык и нажмите :
Затем нужно указать имя проекта и его расположение (можете создать отдельную папку Projects) и нажать :
В следующем диалоговом окне нужно будет нажать .
После всех этих манипуляций, вы увидите пустое рабочее пространство. Вам нужно будет открыть папку Sources в левой части экрана и дважды кликнуть по main.cpp:
Вы увидите, что программа «Hello, world!» уже написана!
Для того, чтобы скомпилировать ваш проект в Code::Blocks, нажмите , либо перейдите в меню и выберите . Если всё пройдет хорошо, то вы увидете следующее во вкладке :
Это означает, что компиляция прошла успешно!
Чтобы запустить скомпилированную программу, нажмите , либо перейдите в меню и выберите . Вы увидите следующее окно:
Это результат выполнения вашей программы.
Трансляторы
Так как текст записанной на Паскале программы не понятен компьютеру, то требуется перевести его на машинный язык. Такой перевод программы с языка программирования на язык машинных кодов называетсятрансляцией (translation — перевод), а выполняется он специальными программами —трансляторами.
Существует три вида трансляторов: интерпретаторы, компиляторы и ассемблеры.
Интерпретатором называется транслятор, производящий пооператорную (покомандную) обработку и выполнение исходной программы.
Компилятор преобразует (транслирует) всю программу в модуль на машинном языке, после этого программа записывается в память компьютера и лишь потом исполняется.
Ассемблеры переводят программу, записанную на языке ассемблера (автокода), в программу на машинном языке.
Любой транслятор решает следующие основные задачи:
• анализирует транслируемую программу, в частности определяет, содержит ли она синтаксические ошибки;
• генерирует выходную программу (ее часто называют объектной или рабочей) на языке команд ЭВМ (в некоторых случаях транслятор генерирует выходную программу на промежуточном языке, например, на языке ассемблера);
• распределяет память для выходной программы (в простейшем случае это заключается в назначении каждому фрагменту программы, переменным, константам, массивам и другим объектам своих адресов участков памяти).
Структура компилятора
Процесс компиляции состоит из следующих этапов:
- Трансляция программы — трансляция всех или только изменённых модулей исходной программы.
- компоновка машинно-ориентированной программы.
Структурные реализации компилятора могут быть следующими:
- И транслятор, и компоновщик могут целиком входит в состав компилятора как исполняемое программы.
- Компилятор сам выполняет лишь трансляцию компилируемой программы, компоновка же программы выполняется вызываемой компилятором отдельной программой-компоновщиком. Практически все современные компиляторы построены по такой схеме.
- Пакет программ, включающий в себя трансляторы с разных языков программирования и компоновщики.
По первой схеме строились самые первые компиляторы, — для современных компиляторов такая схема построения нехарактерна.
По второй схеме построены все без исключения компиляторы с языков высокого уровня. Любой такой компилятор сам выполняет только трансляцию и далее вызывает компоновщик как внешнюю подпрограмму, который и компонует машинно-ориентированную программу. Такая схема построения легко позволяет компилятору работать и в режиме транслятора с соответствующего языка программирования. Этот обстоятельство нередко служит поводом считать компилятор разновидностью транслятора, что естественно неверно, — все современные компиляторы такого типа все же выполняют компоновку, пусть и силами вызываемого компилятором внешнего компоновщика, тогда как транслятор сам никогда не выполняет вызов внешнего компоновщика. Но это же обстоятельство позволяет компилятору с одного языка программирования на фазе компоновки включать в программу написанную на одном языке программирования функции-подпрограммы из уже оттранслированных соответствующим транслятором/компилятором, написанные на ином языке программирования. Так в программу на С/С++ можно вставлять функции написанные например на Pascal или Fortran. Аналогично и напротив написанная на С/С++ функции могут быть вставлены в Pascal- или Fortran-программу соответственно. Это было бы невозможно без поддержки многими современными компиляторами генерации кода вызова процедур (функций) в соответствии с соглашениями иных языков программирования. Например современные компиляторы с языка Pascal помимо организации вызова процедур/функций в стандарте самого Pascal поддерживают организацию вызова процедурой/функцией в соответствии с соглашениями языка С/С++. (Например чтобы на уровне машинного кода написанная на Pascal процедура/функция работала с входными параметрами в соответствии с соглашениями языка С/С++, — оператор объявления такой Pascal-процедуры/Pascal-функции должен содержать ключевое слово cdecl.)
Наконец по третьей схеме построены компиляторы, представляющие собой целые системы, включающие в себя трансляторы с разных языков программирования и компоновщики. Также любой такой компилятор может использовать в качестве транслятора любой способный работать в режиме транслятора компилятор с конкретного языка высокого уровня. Естественно такой компилятор может компилировать программу, разные части исходного текста которой написаны на разных языках программирования. Нередко такие компиляторы управляются встроенным интерпретатором того или иного командного языка. Яркий пример таких компиляторов — имеющийся во всех UNIX-системах (в частности в Linux) компилятор make.
Трансляция программы как неотъемлемая составляющая компиляции включает в себя:
- Лексический анализ. На этом этапе последовательность символов исходного файла преобразуется в последовательность лексем.
- Синтаксический (грамматический) анализ. Последовательность лексем преобразуется в древо разбора.
- Семантический анализ. На этой фазе древо разбора обрабатывается с целью установления его семантики (смысла) — например, привязка идентификаторов к их объявлениям, типам данных, проверка совместимости, определение типов выражений и т. д. Результат обычно называется «промежуточным представлением/кодом», и может быть дополненным древом разбора, новым деревом, абстрактным набором команд или чем-то ещё, удобным для дальнейшей обработки.
- Оптимизация. Выполняется удаление излишних конструкций и упрощение кода с сохранением его смысла. Оптимизация может быть на разных уровнях и этапах — например, над промежуточным кодом или над конечным машинным кодом.
- Генерация кода. Из промежуточного представления порождается код на целевом машинно-ориентированном языке.
Примечания
- ГОСТ 19781-83 // Вычислительная техника. Терминология: Справочное пособие. Выпуск 1 / Рецензент канд. техн. наук Ю. П. Селиванов. — М.: Издательство стандартов, 1989. — 168 с. — 55 000 экз. — ISBN 5-7050-0155-X.; см. также ГОСТ 19781-90
- ↑ Першиков В. И., Савинков В. М. Толковый словарь по информатике / Рецензенты: канд. физ.-мат. наук А. С. Марков и д-р физ.-мат. наук И. В. Поттосин. — М.: Финансы и статистика, 1991. — 543 с. — 50 000 экз. — ISBN 5-279-00367-0.
- ↑ СТ ИСО 2382/7-77 // Вычислительная техника. Терминология. Указ. соч.
- Борковский А. Б. Англо-русский словарь по программированию и информатике (с толкованиями). — М.: Русский язык, 1990. — 335 с. — 50 050 (доп,) экз. — ISBN 5-200-01169-3.
- Толковый словарь по вычислительным системам = Dictionary of Computing / Под ред. В. Иллингуорта и др.: Пер. с англ. А. К. Белоцкого и др.; Под ред. Е. К. Масловского. — М.: Машиностроение, 1990. — 560 с. — 70 000 (доп,) экз. — ISBN 5-217-00617-X (СССР), ISBN 0-19-853913-4 (Великобритания).
- Н. А. Криницкий, Г. А. Миронов, Г. Д. Фролов. Программирование / Под ред. М. Р. Шура-Бура. — М.: Государственное издательство физико-математической литературы, 1963.
Генерация кода
Генерация машинного кода
Большинство компиляторов переводит программу с некоторого высокоуровневого языка программирования в машинный код, который может быть непосредственно выполнен физическим процессором. Как правило, этот код также ориентирован на исполнение в среде конкретной операционной системы, поскольку использует предоставляемые ею возможности (системные вызовы, библиотеки функций). Архитектура (набор программно-аппаратных средств), для которой компилируется (собирается) машинно-ориентированная программа, называется целевой машиной.
Результат компиляции — исполнимый программный модуль — обладает максимально возможной производительностью, однако привязан к конкретной операционной системе (семейству или подсемейству ОС) и процессору (семейству процессоров) и не будет работать на других.
Для каждой целевой машины (IBM, Apple, Sun, Эльбрус и т. д.) и каждой операционной системы или семейства операционных систем, работающих на целевой машине, требуется написание своего компилятора. Существуют также так называемые кросс-компиляторы, позволяющие на одной машине и в среде одной ОС генерировать код, предназначенный для выполнения на другой целевой машине и/или в среде другой ОС. Кроме того, компиляторы могут оптимизировать код под разные модели из одного семейства процессоров (путём поддержки специфичных для этих моделей особенностей или расширений наборов команд). Например, код, скомпилированный под процессоры семейства Pentium, может учитывать особенности распараллеливания инструкций и использовать их специфичные расширения — MMX, SSE и т. п.
Некоторые компиляторы переводят программу с языка высокого уровня не прямо в машинный код, а на язык ассемблера. (Пример: PureBasic, транслирующий бейсик-код в ассемблер FASM.) Это делается для упрощения части компилятора, отвечающей за генерацию кода, и повышения его переносимости (задача окончательной генерации кода и привязки его к требуемой целевой платформе перекладывается на ассемблер), либо для возможности контроля и исправления результата компиляции (в т. ч. ручной оптимизации) программистом.
Генерация байт-кода
Результатом работы компилятора может быть программа на специально созданном низкоуровневом языке двоично-кодовых команд, выполняемых виртуальной машиной. Такой язык называется псевдокодом или байт-кодом. Как правило, он не есть машинный код какого-либо компьютера и программы на нём могут исполняться на различных архитектурах, где имеется соответствующая виртуальная машина, но в некоторых случаях создаются аппаратные платформы, напрямую выполняющие псевдокод какого-либо языка. Например, псевдокод языка Java называется байт-кодом Java и выполняется в Java Virtual Machine, для его прямого исполнения была создана спецификация процессора picoJava. Для платформы .NET Framework псевдокод называется Common Intermediate Language (CIL), а среда исполнения — Common Language Runtime (CLR).
Некоторые реализации интерпретируемых языков высокого уровня (например, Perl) используют байт-код для оптимизации исполнения: затратные этапы синтаксического анализа и преобразование текста программы в байт-код выполняются один раз при загрузке, затем соответствующий код может многократно использоваться без перекомляции.
Динамическая компиляция
Из-за необходимости интерпретации байт-код выполняется значительно медленнее машинного кода сравнимой функциональности, однако он более переносим (не зависит от операционной системы и модели процессора). Чтобы ускорить выполнение байт-кода, используется динамическая компиляция, когда виртуальная машина транслирует псевдокод в машинный код непосредственно перед его первым исполнением (и при повторных обращениях к коду исполняется уже скомпилированный вариант).
Наиболее популярной разновидностью динамической компиляции является JIT. Другой разновидностью является .
CIL-код также компилируется в код целевой машины JIT-компилятором, а библиотеки .NET Framework компилируются заранее.
Указание целевых входных и выходных параметров
Первым делом важно разобраться с тем, как указывать имя и тип создаваемой сборки (т.е., например, консольное приложение по имени MyShell.exe, библиотека кода
по имени MathLib.dll или приложение Windows Presentation Foundation по имени Halo8.ехе). Каждый из возможных вариантов имеет соответствующий флаг, который
нужно передать компилятору csc.ехе в виде параметра командной строки
Обратите внимание, что параметры, передаваемые компилятору командной строки (а также большинству других утилит командной строки), могут сопровождаться префиксом в виде символа дефиса (-) или слеша (/). Выходные параметры, которые может принимать компилятор C# приведены в следующей таблице:
Выходные параметры, которые может принимать компилятор C# приведены в следующей таблице:
| Параметр | Описание |
|---|---|
| /out | Этот параметр применяется для указания имени создаваемой сборки. По умолчанию сборке присваивается то же имя, что у входного файла *.сs |
| /target:exe | Этот параметр позволяет создавать исполняемое консольное приложение. Сборка такого типа генерируется по умолчанию, потому при создании подобного приложения данный параметр можно опускать |
| /target:library | Этот параметр позволяет создавать однофайловую сборку *.dll |
| /target:module | Этот параметр позволяет создавать модуль. Модули являются элементами многофайловых сборок |
| /target:winexe | Хотя приложения с графическим пользовательским интерфейсом можно создавать с применением параметра /target: ехе, параметр /target: winexe позволяет предотвратить открытие окна консоли под остальными окнами |
Чтобы скомпилировать TestApplication.cs в консольное приложение TestApplication.exe, перейдите в каталог, в котором был сохранен файл исходного кода (с помощью флага cd) и введите следующую команду:

Обратите внимание, что здесь C:\myProject — это путь к папке, в которой хранится файл TestApplication.cs. Так же обратите внимание, что здесь флаг /out не был указан явным образом, поэтому исполняемым файл получит
имя TestApplication.ехе из-за того, что именем входного файла является TestApplication
Кроме того,
для почти всех принимаемых компилятором C# флагов поддерживаются сокращенные
версии написания, наподобие /t вместо /target (полный список которых можно увидеть, введя в командной строке команду csc -?).
Теперь можно попробовать запустить приложение TestApplication.ехе из командной строки, введя имя его исполняемого файла:

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




