10.2 Традиционные макросы ¶
Основное различие между традиционными и ISO-макросами заключается в том, что первые расширяются до текста, а не до последовательности токенов. Препроцессор CPP удаляет все начальные и конечные горизонтальные пробелы из текста подстановки макроса перед его сохранением, но сохраняет форму внутренних пробелов.
Одним из последствий является то, что допустимо, чтобы текст замены содержал непарную кавычку (см. Традиционный лексический анализ). Незакрытая строка или символ продолжит текст после вызова макроса. Аналогично, текст в конце расширения макроса может соединиться с текстом после вызова макроса, образуя один токен.
Обычно комментарии удаляются из текста замены после расширения макроса, но если опция -CC передается в командной строке, комментарии сохраняются. (На самом деле, текущая реализация удаляет комментарии даже до сохранения текста замены макроса, но она делает это таким образом, чтобы наблюдаемый эффект был идентичным даже в случае макроса с параметрами.)
Оператор строковой подстановки ‘#’ и оператор склейки токенов ‘##’ не имеют специального значения. Как будет объяснено позже, аналогичный эффект можно получить другим способом. Имена макросов, вложенные в кавычки, как из исходного файла, так и после замены макроса, не расширяются.
Препроцессор CPP заменяет имя объектно-подобного макроса без кавычек на его текст замены, а затем повторно сканирует его на наличие других макросов для замены. В отличие от расширения стандартных макросов, традиционное расширение макросов не имеет механизма для предотвращения рекурсии. Если объектно-подобный макрос появляется без кавычек в тексте замены, он будет повторно заменен во время прохода повторного сканирования, и так далее до бесконечности. GCC обнаруживает расширение рекурсивных макросов, выводит сообщение об ошибке и продолжает после вызова нарушающего макроса.
#define PLUS +
#define INC(x) PLUS+x
INC(foo);
→ ++foo;
Функциональные макросы схожи по форме, но существенно отличаются по поведению от своих ISO-аналогов. Их аргументы заключены в скобки, разделяются запятыми и могут переходить на физические строки. Запятые в вложенных скобках не обрабатываются как разделители аргументов. Аналогично, кавычка в аргументе не может быть оставлена незакрытой; последующая запятая или скобка, которая встречается до закрывающей кавычки, обрабатывается как любой другой символ. Нет механизма для обработки макросов с переменным числом аргументов.
В этой реализации все комментарии из аргументов макроса удаляются, если не задана опция -C. Форма всех остальных горизонтальных пробелов в аргументах сохраняется, включая начальные и конечные пробелы. В частности
f( )
обрабатывается как вызов макроса ‘f’ с одним аргументом, состоящим из одного пробела. Если вы хотите вызвать функциональный макрос, не принимающий аргументов, не оставляйте пробелов между скобками.
Если аргумент макроса переходит на новую строку, новая строка заменяется пробелом при формировании аргумента. Если предыдущая строка содержала незакрытую кавычку, последующая строка наследует состояние кавычки.
Традиционные препроцессоры заменяют параметры в тексте замены своими аргументами независимо от того, находятся ли параметры в кавычках или нет. Это предоставляет способ строковой подстановки аргументов. Например
#define str(x) "x"
str(/* A comment */some text )
→ "some text "
Обратите внимание, что комментарий удаляется, но конечный пробел сохраняется. Вот пример использования комментария для эффекта склейки токенов.
#define suffix(x) foo_/**/x
suffix(bar)
→ foo_bar
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-10.5.0/cpp/Traditional-macros.html