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

Обновлено: 07.07.2024

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

Текст программы, написанный на языке программирования высокого уровня, до того как быть преобразованным в машинные коды, называется исходным кодом (source code). Компилятор (compiler) преобразует исходный код в машинные коды, называемые объектным кодом (object code), то есть программой на выходном языке транслятора. Перед выполнением протекает процесс редактирования связей (linkage editing), когда модули выходной программы объединяются с другими модулями объектного кода, содержащими, например, данные. Результирующий загрузочный модуль – это команды, непосредственно выполняемые компьютером. На Рис. 2.3 показан процесс трансляции кода программы.

Языки программирования, для которых существуют программы-компиляторы, называют компилируемыми языками. К ним относятся Паскаль, С++, Delphi, Fortran и другие.

Интегрированная система Турбо Паскаль состоит из языка программирования и среды программирования. В эту систему входят необходимые части Turbo Pascal:

Интегрированная среда Турбо Паскаль запускается программой TURBO.EXE (Bp.exe)

Контрольные вопросы

1. Понятие языка программирования

2. Классификация языков программирования

3. Понятие виртуальной машины и системы программирования

4. Перечислите основные элементы языка программирования

5. Идентификаторы, их назначение в программе?

6. Операции, типы операций

7. Данные и константы, примеры

8. Переменные, их назначение, приведите примеры

9. Выражения, типы выражений, приведите примеры

10. Понятия оператора,

11. Понятие функции

12. Понятие программы, подпрограммы, программирования

13. Схема преобразования исходного кода, поясните схему

14. Что включает в себя интегрированная среда Турбо Паскаль, поясните каждый пункт и его назначение.

Самостоятельная работа

1. Создайте презентацию на тему:

Эволюция языков программирования.

Раздел 2 . Основы программирования на Турбо Паскаль.

Тема 2.1.1. Элементы языка Pascal. Структура языка.

Структурная схема программы на алгоритмическом языке. Алфавит языка.

После изучения темы студент должен

знать:

- Структурную схему программы;

- Способы описания меток, переменных, типов, констант;

- Объекты алфавита языка;

Уметь

- описывать переменные, метки, константы;

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

I. Структурная схема программы на языке TurboPascal

Любая программа на языке TurboPascal(ТР) состоит из двух основных разделов: раздела описаний данных и раздела операторов, и заканчивается всегда символом «.».

Begin

End.

Раздел описаний может включать в себя подразделы описания меток, констант, типов, переменных, а также подпрограмм, реализуемых в виде процедур или функций. Если в программе используются стандартные или библиотечные модули (Unit), то первой должна стоять директива Uses, в которой перечисляются используемые модули. Рекомендуется всегда включать в программу директиву: Uses CRT;

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

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

Раздел описаний может содержать следующие подразделы:

1. Список имен, используемых модулей.

2.Объявление меток.

3.Объявление констант.

4.Объявление типов.

5.Объявление переменных.

6.Описание процедур и функций.

Не все подразделы обязательны.

Каждый из подразделов раздела описаний начинается своим ключевым словом.

Список имен модулей начинается с ключевого слова USES.

Раздел меток начинается с ключевого слова LABEL, раздел констант - CONST , раздел типов - TYPE , раздел переменных - VAR .

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

Begin . End.

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

Описание меток

Метка представляет собой правильный идентификатор или любое

целое без знака от 1 до 9999. Метки должны быть описаны в подразделе Label. Каждая метка описывается только один раз в каждой программной единице (основной программе или подпрограммах).

Label метка; или Label метка1, метка2, …, меткаN;

В программе метка ставится перед оператором, на который передается управление и отделяется от него символом ":".

Метка : выполняемый оператор;

Примеры описания меток:

Label m1, m2, met1, l1, lab, 125;

Описание переменных

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

Определение переменной должно содержать имя переменной и ее тип, разделенные двоеточием.

VAR имя переменной : тип;

Var x:real; i: byte;

S: char; b: boolean;

Переменные одного типа записываются друг за другом через запятые:

Var a, b, c : real;

Для переменных, описанных в каждой программной единице, отводится определенный объем памяти.

Переменные, описанные в основной (главной) программе, называют глобальными переменными. Общий объем памяти, отведенный под глобальные переменные, не должен превышать 64 Кбайта.

Переменные, описываемые в подпрограммах, называются локальными переменными.

Описание типов

В простейших случаях тип переменных указывается явно, при их описании в разделе Var:

Var Имя переменной: тип;

Можно сопоставить типу некоторое имя и описать его в разделе Type:

Type Имя типа = Тип;

Type Diapason = 1..1000;

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

Далее можно имена типов, введенные в подразделе Type использовать в подразделе Var:

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

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

Язык, на котором представлена входная программа, называется исходным языком, а сама программа — исходным кодом.

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

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

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

Каждый из этих способов преобразования имеет свои достоинства и недостатки.

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

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

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

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

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

Все действия будут производиться на Ubuntu версии 16.04.
Используя компилятор g++ версии:

Состав компилятора g++

Мы не будем вызывать данные компоненты напрямую, так как для того, чтобы работать с C++ кодом, требуются дополнительные библиотеки, позволив все необходимые подгрузки делать основному компоненту компилятора — g++.

Зачем нужно компилировать исходные файлы?

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

Этапы компиляции:

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

driver.cpp:

1) Препроцессинг

Самая первая стадия компиляции программы.

Получим препроцессированный код в выходной файл driver.ii (прошедшие через стадию препроцессинга C++ файлы имеют расширение .ii), используя флаг -E, который сообщает компилятору, что компилировать (об этом далее) файл не нужно, а только провести его препроцессинг:

Взглянув на тело функции main в новом сгенерированном файле, можно заметить, что макрос RETURN был заменен:

В новом сгенерированном файле также можно увидеть огромное количество новых строк, это различные библиотеки и хэдер iostream.

2) Компиляция

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

Ассемблерный код — это доступное для понимания человеком представление машинного кода.

Используя флаг -S, который сообщает компилятору остановиться после стадии компиляции, получим ассемблерный код в выходном файле driver.s:

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

3) Ассемблирование

Так как x86 процессоры исполняют команды на бинарном коде, необходимо перевести ассемблерный код в машинный с помощью ассемблера.

Ассемблер преобразовывает ассемблерный код в машинный код, сохраняя его в объектном файле.

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

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

Получим машинный код с помощью ассемблера (as) в выходной объектный файл driver.o:

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

4) Компоновка

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

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

Получим исполняемый файл driver:

5) Загрузка

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

Запустим нашу программу:

Заключение

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

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

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

Язык, на котором представлена входная программа, называется исходным языком, а сама программа — исходным кодом.

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

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

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

Каждый из этих способов преобразования имеет свои достоинства и недостатки.

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

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

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

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

Оптимизация кода для повышения эффективности

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

В интерпретаторе с этим приходится мириться, но в компиляторе предусмотрено специальное средство ликвидации такой неэффективности.

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

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

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

Структура IDE. Отладка программ

IDE - (англ. Integrated Development Environment,) — интегрированная среда разработки программного обеспечения.

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

Многие современные среды разработки также включают браузер классов, инспектор объектов и диаграмму иерархии классов — для использования при объектно-ориентированной разработке ПО. Хотя и существуют среды разработки, предназначенные для нескольких языков — такие как Eclipse или Microsoft Visual Studio, обычно среда разработки предназначается для одного определённого языка программирования — как например, Visual Basic.

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

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

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

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

Лекция 8.

Основы языка Object Pascal/Delphi

В настоящем курсе рассматриваются основы языка программирования Object Pascal, реализованного в среде программирования Delphi фирмы Borland International.

Описание структуры проекта

Любая программа в Delphi состоит из файла проекта (файл с расширением dpr) и одного или нескольких модулей (файлы с расширениями pas). Каждый из таких файлов описывает программную единицу Object Pascal.

Файл проекта представляет собой программу, написанную на языке Object Pascal и предназначенную для обработки компилятором. Эта программа автоматически создается Delphi и содержит лишь несколько строк. Чтобы увидеть их, запустите Delphi и щелкните по опции Project | View Source главного меню. Delphi покажет окно кода с закладкой Project1, содержащее такой текст:

program Project1;

Uses

Unit1 in 'Unit1.pas' ;

Begin

End.

В окне кода жирным шрифтом выделяются так называемые зарезервированные слова, а курсивом - комментарии (так же выделяются зарезервированные слова и комментарии в книге). Как видим, текст программы начинается зарезервированным словом program и заканчивается словом end с точкой за ним. Замечу, что сочетание end со следующей за ней точкой называется терминатором программной единицы: как только в тексте программы встретится такой терминатор, компилятор прекращает анализ программы и игнорирует оставшуюся часть текста.

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

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

Поскольку речь зашла о комментариях, замечу, что в Object Pascal в качестве ограничителей комментария могут также использоваться пары символов (*, *) и //. Скобки (*. *) допускают вложенные комментарии, а символы // указывают компилятору, что комментарий располагается за ними и продолжается до конца текущей строки:

(*Это тоже комментарий*)

//Все символы до конца этой строки составляют комментарий

Слово Program со следующим за ним именем программы и точкой с запятой образуют заголовок программы. За заголовком следует раздел описаний, в котором программист (или Delphi) описывает используемые в программе идентификаторы. Идентификаторы обозначают элементы программы, такие как типы, переменные, процедуры, функции (об элементах программы мы поговорим чуть позже). Здесь же с помощью предложения, которое начинается зарезервированным словом uses (использовать) программист сообщает компилятору о тех фрагментах программы (модулях), которые необходимо рассматривать как неотъемлемые составные части программы и которые располагаются в других файлах. Строки:

Forms, Unit1 in 'Unitl.pas' ;

указывают, что помимо файла проекта в программе должны использоваться модули Forms и Unit1. модуль Forms является стандартным (т. е. уже известным Delphi), а модуль Unit1 - новым, ранее неизвестным, и Delphi в этом случае указывает также имя файла с текстом модуля (in 'uniti.pas') и имя связанного с модулем файла описания формы .

Собственно тело программы начинается со слова begin (начать) и ограничивается терминатором end с точкой. Тело состоит из нескольких операторов языка Object Pascal. В каждом операторе реализуется некоторое действие - изменение значения переменной, анализ результата вычисления, обращение к подпрограмме и т. п. В теле нашей программы - три исполняемых оператора:

Каждый из них реализует обращение к одному из методов объекта Application. Объектом называется специальным образом оформленный фрагмент программы, заключающий в себе данные и подпрограммы для их обработки. Данные называются полями объекта, а подпрограммы - его методами. Объект в целом предназначен для решения какой-либо конкретной задачи и воспринимается в программе как неделимое целое (иными словами, нельзя из объекта "выдернуть" отдельное поле или метод). Объекты играют чрезвычайно важную роль в современных языках программирования. Они придуманы для того, чтобы увеличить производительность труда программиста и одновременно повысить качество разрабатываемых им программ. Два главных свойства объекта - функциональность и неделимость - делают его самостоятельной или даже самодостаточной частью программы и позволяют легко переносить объект из одной программы в другую. Разработчики Delphi придумали для нас с вами сотни объектов, которые можно рассматривать как кирпичики, из которых программист строит многоэтажное здание программы. Такой принцип построения программ называется объектно-ориентированным программированием (ООП). В объекте Application собраны данные и подпрограммы, необходимые для нормального функционирования Windows-программы в целом. Delphi автоматически создает объект-программу Application для каждого нового проекта. Строка:

означает обращение к методу Initialize объекта Application. Прочитав эту строку, компилятор создаст код, который заставит процессор перейти к выполнению некоторого фрагмента программы, написанного для нас разработчиками Delphi. После выполнения этого фрагмента (программисты говорят: после выхода из подпрограммы) управление процессором перейдет к следующей строке программы, в которой вызывается метод CreateForm и т. д.

Описание структуры модуля

Модули - это программные единицы, предназначенные для размещений фрагментов программ. С помощью содержащегося в них программного кода реализуется вся поведенческая сторона программы. Любой модуль имеет следующую структуру: заголовок, секция интерфейсных объявлений, секция реализации, терминатор. Заголовок открывается зарезервированным словом Unit, за которым следует имя модуля и точка с запятой. Секция интерфейсных объявлений открывается зарезервированным словом Interface, a секция реализации - словом implementation. Терминатором модуля, как и терминатором программы, является end с точкой. Следующий фрагмент программы является синтаксически правильным вариантом модуля:

// Секция интерфейсных объявлений

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

Читайте также: