close
The Wayback Machine - https://web.archive.org/web/20220815235741/https://ru.cppreference.com/w/cpp/language/translation_phases
Пространства имён
Варианты
Действия

Фазы трансляции

Материал из cppreference.com
< cpp‎ | language
 
 
 
 

Исходный файл C++ должен быть препроцессирован компилятором так, как будто последовательно выполняются следующие фазы:

Содержание

[править] Фаза 1

1) Отдельные байты файла исходного кода отображаются (в зависимости от реализации) на символы базового исходного набора символов (до C++23)набора символов перевода (начиная с C++23). В частности, индикаторы конца строки, зависящие от ОС, заменяются символами новой строки.
2) Набор символов исходного файла определяется реализацией (начиная с C++11). Любой символ исходного файла, который не может быть сопоставлен с символом в основном исходном наборе символов, заменяется его универсальным именем символа (с экранированием с помощью \u или \U) или какой-либо формой, определяемой реализацией, которая обрабатывается эквивалентно. (до C++23)
3) Триграфы заменяются соответствующими односимвольными представлениями.
(до C++17)

[править] Фаза 2

1) Всякий раз, когда символ \ (обратная косая черта, backslash) появляется в конце строки (сразу же за которым следует ноль или более пробелов, кроме символа новой строки, за которыми следует (начиная с C++23) символ новой строки), оба символа удаляются, объединяя две физические строки исходного кода в одну логическую строку исходного кода. Это однопроходная операция. Строка, заканчивающаяся двумя символами \, за которой следует пустая строка, не объединяет три строки в одну. Если на этом этапе формируется универсальное имя символа вне необработанных строковых литералов (начиная с C++11), поведение не определено.
2) Если после этого шага непустой файл исходного кода не закончился символом новой строки (независимо от того, не было ли у него символа новой строки изначально или он закончился новой строкой, непосредственно перед которой стояла обратная косая черта), добавляется завершающий символ новой строки.

[править] Фаза 3

1) Файл исходного кода разбивается на комментарии, последовательности пробельных символов и лексемы препроцессора, которые являются следующим:
a) имена заголовочных файлов, такие как <iostream> или "myfile.h" (распознаются только после директивы #include)
c) числа препроцессора
e) символы операций и пунктуаторы (в том числе альтернативные лексемы), такие как +, <<=, new, <%, ##, или and
f) отдельные непробельные символы, которые не входят ни в одну из категорий
2) Отменяются любые преобразования, выполненные во время фаз 1 и 2 между символами " (открывающая и закрывающая двойная кавычка), в любом необработанном строковом литерале.
(начиная с C++11)
(до C++23)
2) Любые преобразования, выполненные во время фазы 2 (объединение строк) между начальной и конечной двойной кавычкой любого сырого строкового литерала, отменяются.
(начиная с C++23)
3) Каждый комментарий заменяется одним пробелом.

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

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

a) символьный литерал (символьная-последовательность-c)
b) строковый литерал (символьная-последовательность-s и символьная-последовательность-r), исключая разделители (символьная-последовательность-d)
c) имя файла для включения (символьная-последовательность-h и символьная-последовательность-q)
(начиная с C++23)

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

int foo = 1;
int bar = 0xE+foo;   // ошибка, недействительное число препроцессора 0xE+foo
int baz = 0xE + foo; // OK
 
int quux = bar+++++baz; // ошибка: распознается как bar++ ++ +baz, а не как bar++ + ++baz.

Исключения из правила максимального куска:

  • Если следующий символ начинает последовательность символов, которая могла бы быть префиксом и открывающей двойной кавычкой необработанного строкового литерала, то следующей лексемой препроцессора будет необработанный строковый литерал. Этот литерал состоит из кратчайшей последовательности символов, которая совпадает с шаблоном необработанного литерала.
#define R "x"
const char* s = R"y"; 		// некорректный необработанный строковый литерал,
                                // не "x" "y"
const char* s2 = R"(a)" "b)";   // необработанный строковый литерал, 
                                // за которым идёт обычный строковый литерал
  • Если следующими тремя символами будут <:: и последующий символ не : и не >, то < рассматривается как лексема препроцессора сама по себе (а не как первый символ альтернативной лексемы <:).
struct Foo { static const int v = 1; };
std::vector<::Foo> x; // OK, <: не рассматривается как альтернативная лексема для [
extern int y<::>;     // OK, эксивалентно extern int y[].
int z<:::Foo::value:>; // OK, int z[::Foo::value];
(начиная с C++11)
  • Лексемы препроцессора, указывающие имена заголовочных файлов, формируются только внутри директивы #include.
std::vector<int> x; // OK, <int> не имя заголовочного файла

[править] Фаза 4

1 ) Выполняется препроцессор.
2 ) Каждый файл, введённый с помощью директивы #include, рекурсивно проходит фазы 1-4.
3 ) В конце этой фазы все директивы препроцессора удаляются из исходного кода.

[править] Фаза 5

1) Все символы в символьных литералах и строковых литералах преобразуются из исходного набора символов в набор символов выполнения (который может быть многобайтовым набором символов, например UTF-8, если 96 символов базового исходного набора символов имеют однобайтовые представления).
2) Управляющие последовательности и универсальные имена символов в символьных литералах и строковых литералах, за исключением необработанных литералов, расширяются и преобразуются в набор символов выполнения. Если символ, указанный универсальным символьным именем, не является элементом набора символов выполнения, результат определяется реализацией, но гарантированно не является нулевым (широким) символом.

Примечание: преобразование, выполняемое на этом этапе, в некоторых реализациях может управляться параметрами командной строки : GCC и Clang используют -finput-charset и -fwide-exec-charset для указания соответственно кодировок исходного и исполняемого набора символов в строковых и символьных литералах , которые не имеют префикса, указывающего кодировку (начиная с C++11). Visual Studio, начиная с Visual Studio 2015 Обновление 2, использует параметры /source-charset и /execution-charset соответственно.

[править] Фаза 6

Смежные строковые литералы объединяются.

[править] Фаза 7

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

[править] Фаза 8

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

[править] Фаза 9

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

[править] Примечания

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

[править] Отчёты о дефектах

Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:

Номер Применён Поведение в стандарте Корректное поведение
CWG 787 C++98 поведение было неопределённым, если непустой исходный файл
не заканчивался символом новой строки в конце фазы 2
в этом случае добавляется
завершающий символ новой строки
CWG 1775 C++11 формирование универсального имени символа внутри
необработанного строкового литерала на этапе 2 приводило к
неопределённому поведению
чётко определено

[править] Ссылки

  • C++20 стандарт (ISO/IEC 14882:2020):
  • 5.2 Фазы трансляции [lex.phases]
  • C++17 стандарт (ISO/IEC 14882:2017):
  • 5.2 Фазы трансляции [lex.phases]
  • C++14 стандарт (ISO/IEC 14882:2014):
  • 2.2 Фазы трансляции [lex.phases]
  • C++11 стандарт (ISO/IEC 14882:2011):
  • 2.2 Фазы трансляции [lex.phases]
  • C++03 стандарт (ISO/IEC 14882:2003):
  • 2.1 Фазы трансляции [lex.phases]
  • C++98 стандарт (ISO/IEC 14882:1998):
  • 2.1 Фазы трансляции [lex.phases]

[править] Смотрите также

Документация по C для Фазы трансляции