Какие этапы компиляции программы на C++?

Определены ли стандартом этапы компиляции программы на C++?

Если так, то кто они?

Если нет, ответ для широко используемого компилятора (я бы предпочел MSVS) был бы отличным.

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

РЕДАКТИРОВАТЬ: я знаю, что делают компиляция, связывание и предварительная обработка, меня больше всего интересуют другие и порядок. Объяснения для них, конечно, также приветствуются, поскольку я, возможно, не единственный, кто заинтересован в ответе.


person Luchian Grigore    schedule 12.01.2012    source источник
comment
Вот страница руководства по внутреннему устройству GCC. Я думал, что он использует язык, который вы хотели, но, очевидно, нет. Если вы заглянете в исходный код GCC, то увидите множество отдельных проходов оптимизации. Я бы предположил, что нет, стандарт определяет, что ему нужно для достижения, а не то, как он должен это делать, и что лучшим выбором будет академический курс или учебник по созданию компилятора - я уверен, что вокруг много.   -  person Rup    schedule 12.01.2012
comment
@sharptooth Я откатил вопрос - я считаю, что это название гораздо легче найти людям, которые ищут то же самое.   -  person Luchian Grigore    schedule 12.01.2012
comment
@Luchian Grigore: Хорошо, я просто подумал, что изменение названия действительно имело значение - как это обычно делается по сравнению с тем, что Стандарт говорит о том, как это должно быть сделано. В любом случае это ваш вопрос, вам решать.   -  person sharptooth    schedule 12.01.2012
comment
Все, что выходит за рамки того, что прямо указано в стандарте, выглядит как деталь реализации, которая не должна иметь большого значения. Это чисто из любопытства или вы пытаетесь решить проблему?   -  person Cody Gray    schedule 12.01.2012
comment
Отрицание, гнев, торг, депрессия, принятие.   -  person Keith Thompson    schedule 04.01.2016


Ответы (3)


Определены ли стандартом этапы компиляции программы на C++?

И да и нет.

Стандарт C++ определяет 9 фаз трансляции. Цитата из проекта N3242 (10 МБ PDF) от 28 февраля 2011 г. (до выпуска официального стандарта C++11), раздел 2.2:

Приоритет синтаксических правил перевода определяется следующими фазами [см. сноску].

  1. Символы физического исходного файла сопоставляются способом, определяемым реализацией, с базовым исходным набором символов (с введением символов новой строки для индикаторов конца строки), если это необходимо. [СНИП]
  2. Каждый экземпляр символа обратной косой черты (\), за которым непосредственно следует символ новой строки, удаляется, объединяя физические исходные строки в логические исходные строки. [СНИП]
  3. Исходный файл разбивается на токены предварительной обработки (2.5) и последовательности пробельных символов (включая комментарии). [СНИП]
  4. Выполняются директивы предварительной обработки, расширяются вызовы макросов и выполняются выражения унарных операторов _Pragma. [СНИП]
  5. Каждый элемент исходного набора символов в символьном литерале или строковом литерале, а также каждая управляющая последовательность и универсальное имя-символа в символьном литерале или строковом литерале, отличном от исходного, преобразуются в соответствующий элемент набора символов выполнения; [СНИП]
  6. Смежные токены строкового литерала объединяются.
  7. Пробелы, разделяющие токены, больше не имеют значения. Каждый токен предварительной обработки преобразуется в токен. (2.7). Полученные токены синтаксически и семантически анализируются и переводятся как единица перевода. [СНИП]
  8. Переведенные единицы перевода и единицы воплощения объединяются следующим образом: [SNIP]
  9. Все ссылки на внешние сущности разрешаются. Компоненты библиотеки связаны для удовлетворения внешних ссылок на объекты, не определенные в текущем переводе. Все такие выходные данные транслятора собираются в образ программы, который содержит информацию, необходимую для выполнения в его среде выполнения.

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

Как видно из маркеров [SNIP], я не цитировал весь раздел, а достаточно, чтобы донести мысль.

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

Фазы 1-6 более или менее соответствуют препроцессору, 7 — тому, что вы обычно считаете компиляцией, 8 — работе с шаблонами, а 9 — компоновке.

(Этапы перевода C аналогичны, но № 8 опущен.)

person Keith Thompson    schedule 12.01.2012
comment
FWIW, в gcc фаза 6 не является частью предварительной обработки (gcc -E не объединяет смежные строковые литералы). У меня нет другого компилятора для сравнения. - person Steve Jessop; 12.01.2012
comment
Сноска на этой странице гласит: Реализации должны вести себя так, как если бы эти отдельные фазы происходили, хотя на практике разные фазы могут быть объединены. - person rodrigo; 12.01.2012
comment
@rodrigo: я уже цитировал эту сноску. Вы, вероятно, пропустили это, потому что я не поставил это внизу цитаты. Я только что переместил его туда. - person Keith Thompson; 13.01.2012

9 так называемых «этапов перевода» перечислены в стандарте в [lex.phases] (2.2 в C++11, 2.1 в C++03).

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

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

Он также не дает никаких подробностей о синтаксическом анализе, он просто говорит: «Полученные токены синтаксически и семантически анализируются и переводятся». Это потому, что все главы 3-15 необходимы для заполнения этой детали.

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

person Steve Jessop    schedule 12.01.2012

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

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

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

person Liam M    schedule 12.01.2012