Правила того, когда в C требуются пробелы, не указаны явно, но являются следствием того, как C анализируется.Правила для этого довольно сложны, так как они включают в себя несколько этапов анализа и некоторые исключения для различных ситуаций.Если вы пишете компилятор C, вам необходимо использовать стандарт C в качестве справочного материала.
C 2018 5.1.1.2 определяет фазы перевода (перефразирование и обобщение, а не точные кавычки):
Многобайтовые символы физического исходного файла сопоставляются с исходным набором символов.Последовательности триграфа заменяются односимвольными представлениями.
Строки, продолженные обратной косой чертой, объединяются.
Исходный файл преобразуется из символов в предварительную обработкутокены и пробельные символы - каждая последовательность символов, которая может быть токеном предварительной обработки, преобразуется в токен предварительной обработки, и каждый комментарий становится одним пробелом.
Предварительная обработка выполняется (выполняются директивыи макросы раскрываются).
Исходные символы в константах символов и строковых литералах преобразуются в члены набора символов выполнения.
Смежная строкаЛитералы объединяются.
Пробелы отбрасываются.«Каждый токен предварительной обработки преобразуется в токен.Полученные токены синтаксически и семантически анализируются и переводятся как единица перевода ». (Этот цитируемый текст является основной частью компиляции C, как мы ее думаем!)
Программа связанастать исполняемым файлом.
Прежде всего, когда в исходном коде C требуются пробелы, определяется фаза 3 - формирование токенов предварительной обработки.Это указано в C 2018 6.4.Грамматика для токенов предварительной обработки дана в параграфе 1 (подробнее об этом ниже), а параграф 4 говорит нам:
Если входной поток был проанализирован в токены предварительной обработки до заданного символа, следующийТокен предварительной обработки - это самая длинная последовательность символов, которая может составлять токен предварительной обработки.Из этого правила есть одно исключение: токены предварительной обработки имени заголовка распознаются только в директивах предварительной обработки #include
и в определенных местах реализации в директивах #pragma
.В таких контекстах последовательность символов, которые могут быть либо именем заголовка, либо строковым литералом, распознается как первый.
Параграф 1 говорит нам, что токен предварительной обработки является одним из заголовка-имя , идентификатор , номер pp , символьная константа , строковый литерал , пунктуатор или символ, не являющийся пробелом, который не является одним из предшествующих элементов.
Затем в последующих подразделах 6.4 описывается, как выглядят эти токены.
Этап 3 вызывает два правила длявам нужен пробел, который по существу:
- Если исходный код будет проанализирован, согласно приведенным выше правилам, как один токен предварительной обработки, где вам нужно два, то вы должны вставить пробел, где вы хотитеПервый токен заканчивается.
- Если для ввода комментария используются
/
и *
, отличные от /*
, поместите между ними пробел.
Фаза 4 вызывает другуюправить.Поскольку 6.10.3 3 говорит: «В идентификаторе объекта-подобного макроса между идентификатором и списком замены должен быть пробел», вам нужен пробел, чтобы различать функциональный макрос:
#define foo(x) (3*(x)) // Macro that acts on argument x.
#define foo (x) // Macro that expands to `(x)`.