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

Являются ли этапы компиляции программы на С++, указанной стандартом?

Если да, то каковы они?

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

Я говорю о предварительной обработке, токенизации, разборе и т.д. Каков порядок их выполнения и что они делают в частности?

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

Ответ 1

Являются ли этапы компиляции программы на С++, указанной стандартом?

Да и нет.

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

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

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

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

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

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

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

(C-сдвиговые фазы аналогичны, но # 8 опущено.)

Ответ 2

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

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

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

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

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

Ответ 3

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

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

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