Spec-Zone.ru › GCC 7 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 принимает такие символы только в форматах ‘\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) также являются «другими» в данной реализации. Это изменится, когда в GCC будет добавлена надлежащая поддержка наборов символов национальных языков.

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-7.5.0/cpp/Tokenization.html

Spec-Zone.ru

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