Процесс компиляции программы что это

Процесс компиляции программ на языке C (Семинары)

Файл «Процесс компиляции программ на языке C» внутри архива находится в папке «Семинары». Документ из архива «Семинары», который расположен в категории » «. Всё это находится в предмете «архитектура эвм» из раздела «», которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .

Онлайн просмотр документа «Процесс компиляции программ на языке C»

Текст из документа «Процесс компиляции программ на языке C»

Процесс Си-компиляции — Обзор

Цель этого приложения — описать фазы компиляции программ на языке C и научить настраивать компилятор под ваши нужды. Компилятор C преобразует исходный текст на языке C в кодах ASCII в выполняемый объектный код.

Процесс компиляции разделен на четыре фазы: . Препроцессор: — Осуществляет вставку исходных текстов из других файлов (#include) — Раскрывает макроопределения (#define) — Осуществляет условную обработку исходного файла (#ifdef) — Уничтожает комментарии . Транслятор (компилятор) — Проверяет текст на отсутствие синтаксических ошибок — Преобразует конструкции языка C в конструкции ассемблера — Выполняет машинно-независимые и машинно-зависимые оптимизации — Генерирует отладочную информацию. . Ассемблер — Преобразует конструкции языка ассемблера в машинные команды — Генерирует объектный модуль и списки экспорта и импорта (списки внешних символов) — У некоторых компиляторов этот этап исполняется той же командой, что и трансляция . Редактор связей — Осуществляет сборку объектных файлов в загружаемый модуль — Просматривает библиотеки для разрешения внешних ссылок Для Solaris 10 доступны два компилятора: GNU Compiler Collection (GCC) и SunStudio. Оба компилятора доступны бесплатно, но на разных условиях: от компилятора GCC также доступны исходные тексты.

Что такое компиляция, линковка? Что такое run time?

Оба компилятора включают в себя компиляторы C (с поддержкой диалектов Kernigan когда будут обсуждаться особенности других форм запуска компилятора, это будет оговариваться отдельно. Команда cc — это управляющая программа, которая последовательно вызывает с помощью fork и exec другие программы, реализующие фазы процесса компиляции.

Каждой фазе соответствует свои опции, и у каждой фазы свои сообщения об ошибках. Раздел ФАЙЛЫ на странице Руководства cc(1) указывает, где может быть найдена каждая исполняемая фаза. В общем случае, фазы процесса компиляции не должны вызываться явно. Их вызов осуществляет команда cc(1). Каждая фаза использует файлы или программные каналы для передачи своего вывода следующей фазе.

Формат команды сс

Команда cc имеет формат:

cc [опции] file1.c [file2.c …]

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

В зависимости от расширения файла, компилятор автоматически определяет, что с ним следует делать: файлы с расширением .c обрабатываются всеми фазами компиляции, начиная с препроцессора и транслятора, файлы с расширением .s — начиная с ассемблера, файлы с расширениями .o, .a и .so сразу передаются редактору связей. Простой способ собрать программу из нескольких модулей исходного текста — это передать компилятору список всех этих модулей.

Как работает язык программирования(Компилятор)? Основы программирования.

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

Фаза трансляции занимает много времени, поэтому невыгодно перекомпилировать те файлы исходного текста, которые не менялись. При компиляции программ, состоящих из большого количества файлов исходных текстов, обычно каждый файл компилируют с ключом -c (этот ключ приводит к тому, что редактор связей не вызывается, зато сохраняется объектный модуль в файле с расширением .o), а затем вызывают редактор связей отдельным вызовом команды cc. Это позволяет сэкономить время компиляции, вызывая компилятор только для тех файлов, которые были изменены, и используя старые объектные модули от тех файлов, которые не изменялись. Обычно для координации такой раздельной компиляции используют программу make(1), которая будет кратко описана далее в этом приложении.

Процесс Си-компиляции — Фаза препроцессора

Первая фаза компиляции — препроцессор языка C. Выходные данные препроцессора — это еще ASCII текст (операторы языка C). Все, что делает препроцессор, это текстовые вставки и замены Следующие опции позволяют завершить процесс компиляции после препроцессора: -P — выходные данные записываются в файл name.i. -E — выходные данные записываются в стандартный вывод. Замечание: Команда, исполняющая эту фазу, описана на странице cpp(1) Справочного руководства пользователя.

Директивы препроцессора

Все операторы препроцессора — это полные строки. Директивы препроцессора начинаются с символа # и заканчиваются или началом комментария. Можно соединить несколько строк вместе, набрав обратную косую черту () в конце соединяемых строк.

Перед символом # или после него может стоять один или несколько пробелов или табуляций, но не должно быть символов, не являющихся пробелами Зарезервированные препроцессором слова (например, define или include), расположены непосредственно справа от #, возможно (но не обязательно) отделенные от этого символа одним или несколькими пробелами. В директиве #include может быть задано абсолютное или относительное путевое имя файла.

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

В C++ также используют расширения .hpp или файлы заголовка без расширений. #include «header.h» файл ищется в директории исходного файла, затем в стандартной директории /usr/include #include файл ищется только в /usr/include #include «/usr/header.h» файл задается путевым именем Вставляемые файлы обычно содержат директивы #define для констант и макроопределений, а также директивы #include для других файлов, описания типов, структур, классов и шаблонов C++, объявления глобальных переменных и функций. Достаточно часто вставляемые файлы используются для описания в одном месте структур и параметров, используемых в нескольких файлах исходных текстов.

Читайте также:
Что за программа easeus data recovery wizard

Директивы #define часто используются, чтобы сделать программу более читабельной. Эти имена имеют такую же форму, как идентификаторы языка C. Чтобы выделить эти имена в тексте, их обычно набирают заглавными буквами. Область действия имени — от соответствующего #define до конца файла или до команды #undef, которая отменяет макроопределение.

Макроопределение имеет форму: #define macro_name(param_1. param_n) token_string. Списка параметров может не быть. Не должно быть пробела между macro_name и (.Макросы и символьные имена могут быть определены в терминах ранее определенных макросов и/или символов. Макроопределения также называют символами препроцессора.

Встретив macro_name в тексте, препроцессор заменяет его на token_string, осуществляя подстановку аргументов и выдавая ошибку, если количество переданных аргументов не соответствует заданному в объявлении. Препроцессор не проверяет соответствия выполненной подстановки синтаксису языка C. Если SYMBOL дважды определяется с помощью #define без #undef между ними, то препроцессор может выводить предупреждение «SYMBOL redefined». При этом действует последнее определение.

Условная компиляция

Во время фазы препроцессора можно проверить какое-либо условие с помощью директив #if, #ifdef или #ifndef. Все три формы завершаются директивой #endif. Дополнительно, между началом и концом конструкции может появиться #else . Условие оператора #if expression считается истинным, если expression имеет ненулевое значение.

Условие директивы #ifdef identifier считается истинным, если identifier определен при помощи директивы #define (при этом не имеет значения, как именно он был определен) и не был отменен при помощи директивы #undef. Условие оператор #ifndef identifier считается истинным, если identifier не был определен. #ifdef identifier — это сокращенная форма записи #if defined(identifier), а #ifndef — соответственно, #if !defined(identifier).

Если условие директивы #if, #ifndef или #ifdef истинно, операторы между #if и соответствующими #else или #endif обрабатываются (передаются на вход компилятору), а операторы между #else и #endif (если существуют) заменяются пустыми строками или директивой #line. Это необходимо, чтобы компилятор правильно определял номера строк исходного файла при выдаче отладочной информации и сообщений об ошибках.

Если проверяемое условие ложно, операторы между #if и соответствующим #else или #endif заменяются пустыми строками. Выполняются, если существуют, операторы между #else и #endif. Выражение, используемое в #if может содержать в качестве переменных макроопределения (но не переменные языка C) и любые операции языка Си (логические, арифметические, отношения). Помните, что #if SYMBOL и #if SYMBOL != 0 вырабатывают одинаковый результат. В следующем примере Z получит значение 10, если во время работы препроцессора X больше чем 3, или Y имеет ненулевое значение. X и Y — ранее определенные параметры:

#if X > 3 || Y #define Z 10 #else #define Z 20 #endif

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

Заранее определенные символы препроцессора

Препроцессор автоматически определяет ряд символов, которые могут быть использованы в директивах условной компиляции. Эти символы определяют . Тип аппаратной архитектуры, например, i386 или sparc . Версию ОС . Версию компилятора и поддерживаемые компилятором диалекты языка . Имя текущего обрабатываемого файла: __FILE__ заменяется именем текущего исходного файла, заключенным в двойные кавычки. . Номер обрабатываемой строки: __LINE__ заменяется целым номером текущей строки в формате целого десятичного числа. При задании некоторых ключей компиляции, например, -mt у компилятора SunStudio или -threads у GCC, препроцессор также может определять дополнительные символы, указывающие, что компилируется многопоточная программа. Эти заранее определенные символы могут быть использованы, чтобы сделать программы на Си более переносимыми.

Опции препроцессора

Препроцессор вызывается автоматически на первой фазе процесса компиляции. Следующие опции распознаются командой компилятора (cc) и передаются препроцессору. -P Работает только препроцессор (вывод пишется в файл с расширением .i) -E Работает только препроцессор (вывод пишется в файл стандартного вывода) -Dname=def Определяет идентификатор.

Эквивалентно директиве #define name def в начале файла. Например: cc -DSIZE=10 -DSYM=5 -DDEBUG prog.c. Замечание: если =def опущено, это эквивалентно -Dname=1 -Uname Отменяет определение идентификатора name. Используется для отмены автоматически определяемых препроцессорных символов. Например: cc -E -Ui386 -Du370 prog.c. -Idir Добавляет dir в список директорий, в которых будут искаться вставляемые файлы.

Для директивы #include «file.h» поиск ведется сначала в директории, где хранятся исходные файлы, затем в dir, и в последнюю очередь в стандартной директории /usr/include. Для директивы #include поиск ведется сначала в dir и затем в стандартной директории. Пример:

cc -I$HOME/myproject/include -I..prog.c

-C Не удаляет комментарии

Сообщения об ошибках препроцессора

Препроцессор может выдавать сообщения об ошибках. Например: 1. Использование макроса со слишком малым или слишком большим количеством аргументов приведет к выводу сообщения argument mismatch. 2. Неправильное написание ключевого слова препроцессора (например, #inclde) вызовет сообщение undefined control. 3. Использование имени несуществующего файла или неправильно написанное имя файла в #include приведет к сообщению Can’t find include file. 4. Отсутствие двойных кавычек или < >вокруг имени файла в #include вызовет сообщение bad include syntax.

Процесс компиляции — Фаза транслятора

Второй фазой процесса компиляции является транслятор или компилятор. Входом для него является программа, обработанная препроцессором. Выход — программа на ассемблере, «родном» для целевого компьютера.

Источник: studizba.com

Этапы компиляции. Общая схема работы компилятора

На рис. 1 представлена общая схема работы компилятора. Из нее видно, что в целом процесс компиляции состоит из двух основных этапов: анализа и синтеза.

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

На этапе синтеза на основании внутреннего представления программы и информации, содержащейся в таблице идентификаторов, порождается текст результирующей программы. Результатом этого этапа является объектный код (модуль). Если программа обращалась к функциям и данным другого модуля, компилятор транслирует эти обращения во внешние ссылки (external reference). Если же программа предоставляет доступ к своим данным и функциям другим программам, каждый доступный элемент представляется как внешнее имя (external name). Объектные модули хранят эти внешние имена и ссылки в структуре данных, называемой таблицей имен (symbol table).

Читайте также:
Программа бандикам что это

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

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

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

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

Рассмотрим основные фазы компиляции.

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

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

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

Задачи семантического анализа:

· каждый используемый в программе идентификатор должен быть описан, но не более одного раза в одной зоне описания;

· При вызове функций число фактических параметров и их типы должны соответствовать числу и типам формальных параметров;

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

Рис. 1. Общая схема работы компилятора.

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

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

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

Источник: studopedia.ru

Процесс компиляции программ на C++

Процесс компиляции программ на C++

2018-04-03 в 11:48, admin , рубрики: c++, Компиляторы, Программирование

Цель данной статьи:

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

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

$ g++ —version g++ (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609

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

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

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

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

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

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

driver.cpp:

#include using namespace std; #define RETURN return 0 int main()

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

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

Препроцессор — это макро процессор, который преобразовывает вашу программу для дальнейшего компилирования. На данной стадии происходит происходит работа с препроцессорными директивами. Например, препроцессор добавляет хэдеры в код (#include), убирает комментирования, заменяет макросы (#define) их значениями, выбирает нужные куски кода в соответствии с условиями #if, #ifdef и #ifndef.
Хэдеры, включенные в программу с помощью директивы #include, рекурсивно проходят стадию препроцессинга и включаются в выпускаемый файл. Однако, каждый хэдер может быть открыт во время препроцессинга несколько раз, поэтому, обычно, используются специальные препроцессорные директивы, предохраняющие от циклической зависимости.

Читайте также:
Что за программа monkey s audio

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

g++ -E driver.cpp -o driver.ii

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

int main()

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

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

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

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

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

$ g++ -S driver.ii -o driver.s
driver.s

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

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

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

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

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

$ as driver.s -o driver.o

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

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

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

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

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

$ g++ driver.o -o driver // также тут можно добавить и другие объектные файлы и библиотеки

5) Загрузка

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

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

$ ./driver // Hello, world!

Заключение

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

Источник: www.pvsm.ru

Ассемблер. Компиляция и компоновка

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

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

Начнем с компиляции под DOS

В DOS существовало два вида исполняемых файлов: *.com и *.exe. Файлы *.com характеризуются тем, что весь код программы, данные и место для стека содержится в одном сегменте памяти, причем код программы должен находиться в сегменте со смещением в 256 байт (100h). Файлы формата *.exe не имеют ограничений на количество используемых сегментов памяти.

Для компиляции под DOS я использую компилятор MASM версии 6.11 и TASM версии 5.3

Компиляция и компоновка файлов *.com с помощью TASM:

tasm32bintasm53.exe test.asm, test.obj, test.lst /z /t /ml /m2 /l tasm32bintlink.exe test.obj /x /t /3

Компиляция и компоновка файлов *.exe с помощью TASM:

tasm32bintasm53.exe test.asm, test.obj, test.lst /z /t /ml /m2 /l tasm32bintlink test.obj, test.exe /x /3

Описание всех параметров можно получить, выполнив в командной строке tasm53.exe /? и tlink.exe /?

Компилятор MASM позволяет выполнять компиляцию и компоновку через вызов своей программы-оболочки ml.exe

Компиляция файлов *.com с помощью MASM:

masm611binml.exe test.asm /AT /Fl /link

Компиляция файлов *.exe с помощью MASM:

masm611binml.exe test.asm /Fl /link

Компиляция под Windows

Для компиляции под Windows используются компилятор MASM32 и TASM32.

Компиляция и компоновка файлов *.exe с помощью TASM32:

tasm32bintasm32.exe test.asm, test.obj, test.lst /zi /t /ml /m3 /q tasm32bintlink32 /x /Tpe /ap /c /V4.0 test.obj, test.exe, , user32.lib

Компиляция файлов *.exe с помощью MASM32:

masm32binml.exe /c /coff test.asm masm32binlink.exe /SUBSYSTEM:WINDOWS test.obj

После компиляции и компоновки у вас должен получиться исполняемый файл *.exe или *.com формата, в зависимости от того, какие параметры вы задавали компилятору.

Источник: sysprog.ru

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru