Spec-Zone.ru › GCC 11 CPP

Следующее: Язык предобработки, Предыдущее: Первичная обработка, Вверх: Обзор [Содержание][Индекс]

1.3 Токенизация ¶

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

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

После разбиения входного файла на токены границы токенов никогда не меняются, за исключением случаев использования оператора предобработки ‘##’ для склеивания токенов. См. Конкатенация. Например,

#define foo() bar
foo()baz
     → bar baz
not
     → barbaz

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

Токены предобработки делятся на пять основных классов: идентификаторы, числа предобработки, строковые литералы, пунктуаторы и прочие. Идентификатор – это то же самое, что и идентификатор в C: любая последовательность букв, цифр или подчеркиваний, начинающаяся с буквы или подчеркивания. Ключевые слова языка C не имеют значения для препроцессора; они являются обычными идентификаторами. Например, вы можете определить макрос, имя которого является ключевым словом. Единственный идентификатор, который можно считать ключевым словом предобработки, это defined. См. Определено.

Это в основном справедливо для других языков, использующих препроцессор C. Однако некоторые ключевые слова C++ имеют значение даже в препроцессоре. См. Именованные операторы C++.

В стандарте C 1999 года идентификаторы могут содержать буквы, которые не входят в «базовый набор символов исходного кода», по усмотрению реализации (например, буквы с диакритическими знаками, греческие буквы или китайские иероглифы). Это может быть сделано с расширенным набором символов или с помощью escape-последовательностей ‘\u’ и ‘\U’.

В качестве расширения GCC рассматривает ‘$’ как букву. Это делается для совместимости с некоторыми системами, такими как VMS, где ‘$’ обычно используется в именах системных функций и объектов. ‘$’ не является буквой в строго конформном режиме или если вы указали опцию -$. См. Вызов.

Определение числа предобработки довольно необычное. К этой категории относятся все обычные целочисленные и с плавающей точкой константы, ожидаемые от C, а также ряд других элементов, которые первоначально могут не распознаваться как числа. Формально, числа предобработки начинаются с необязательной точки, обязательной десятичной цифры, а затем продолжаются любой последовательностью букв, цифр, подчеркиваний, точек и экспоненциальных частей. Экспоненциальные части – это двухсимвольные последовательности ‘e+’, ‘e-’, ‘E+’, ‘E-’, ‘p+’, ‘p-’, ‘P+’ и ‘P-’ (экспоненциальные части, начинающиеся с ‘p’ или ‘P’, используются для шестнадцатеричных констант с плавающей точкой).

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

Возможны ситуации, когда числа предобработки могут привести к неправильной интерпретации программ. Например, 0xE+12 является числом предобработки, которое не переводится ни в одну корректную числовую константу, поэтому является синтаксической ошибкой. Это не означает 0xE + 12, что, возможно, вы и имели в виду.

Строковые литералы – это строковые константы, символьные константы и имена файлов заголовков (аргумент ‘#include’).2 Строковые константы и символьные константы просты: "…" или '…'. В обоих случаях вложенные кавычки должны быть экранированы обратной косой чертой: '\'' – это символьная константа для ‘'’.

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

Имена файлов заголовков либо похожи на строковые константы, "…", либо записываются в угловых скобках вместо этого, <…>. В обоих случаях обратная косая черта – это обычный символ. Нет способа экранировать закрывающую кавычку или угловую скобку. Препроцессор ищет файл заголовка в различных местах, в зависимости от используемого формата. См. Действие включения.

Ни один строковый литерал не может выходить за пределы строки. Вместо этого можно использовать продолженные строки или конкатенацию строковых констант.

Пунктуаторы – это все обычные знаки препинания, имеющие значение для C и C++. Все, кроме трёх знаков препинания в ASCII, являются пунктуаторами C. Исключение составляют ‘@’, ‘$’ и ‘`’. Кроме того, все двух- и трёхсимвольные операторы являются пунктуаторами. Также есть шесть диграфов, которые стандарт C++ называет альтернативными токенами, которые представляют собой просто альтернативные способы написания других пунктуаторов. Это вторая попытка обойти проблему отсутствия пунктуации в устаревших системах. В отличие от триграфов, это не имеет негативных последствий, но не покрывает такой же объём.

Digraph:        <%  %>  <:  :>  %:  %:%:
Punctuator:      {   }   [   ]   #    ##

Любой другой одиночный байт считается «другим» и передаётся препроцессору без изменений. Компилятор C почти наверняка отклонит исходный код, содержащий токены «другие». В ASCII единственными символами «другие» являются ‘@’, ‘$’, ‘`’ и управляющие символы, отличные от NUL (все биты равны нулю). (Обратите внимание, что ‘$’ обычно рассматривается как буква.) Также все байты с установленным старшим битом (числовой диапазон 0x7F–0xFF), которые не были успешно интерпретированы как часть расширенного символа в кодировке входных данных, также являются «другими» в данной реализации.

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

#define X^@1
#define X 1

(где ‘^@’ – ASCII NUL). В строковых и символьных константах NUL сохраняются. В последних двух случаях препроцессор выводит сообщение об ошибке.

Примечания

(2)

В стандарте C термин строковый литерал используется только по отношению к тому, что мы называем строковыми константами.

Следующее: Язык предобработки, Предыдущее: Первичная обработка, Вверх: Обзор [Содержание][Индекс]

© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-11.4.0/cpp/Tokenization.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API