10.2 Традиционные макросы
Основное различие между традиционными и ISO-макросами заключается в том, что первые расширяются до текста, а не до последовательности токенов. CPP удаляет все начальные и конечные горизонтальные пробелы из текста замены макроса перед его сохранением, но сохраняет форму внутренних пробелов.
Следствием этого является то, что в тексте замены допустимо наличие непарной кавычки (см. Традиционный лексический анализ). Незакрытая строка или символьная константа продолжается в текст, следующий за вызовом макроса. Аналогично, текст в конце расширения макроса может слиться с текстом после вызова макроса, образуя один токен.
Обычно комментарии удаляются из текста замены после расширения макроса, но если опция -CC передаётся в командной строке, комментарии сохраняются. (На самом деле, текущая реализация удаляет комментарии даже перед сохранением текста замены макроса, но она делает это таким образом, что наблюдаемый эффект идентичен даже в случае макросов типа функций.)
Оператор строкового преобразования ISO ‘#’ и оператор склеивания токенов ‘##’ не имеют особого смысла. Как объяснено позже, подобный эффект этих операторов можно получить другим способом. Имена макросов, вставленные в кавычки, как из исходного файла, так и после замены макроса, не расширяются.
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-14.2.0/cpp/Traditional-macros.html