Простейшее решение этой проблемы — избегать использования ++х как аргумента макроса. Вообще лучше не применять в макросах операции инкремента и декремента. Следует отметить, что выражение++х будет работать в качестве аргумента функции, т.к. оно вычисляется как значение 6, которое затем передается функции.
Создание строк из аргументов макроса: операция #
Рассмотрим следующий функциональный макрос:
#define PSQR(X) printf("Квадрат X равен %d.\n", ((X)*(X)));
Предположим, что этот макрос используется следующим образом:
PSQR(8);
Вот каким будет вывод:
Квадрат X равен 64.
Обратите внимание, что определение X в строке, заключенной в двойные кавычки, трактуется как обычный текст, а не лексема, которую можно заменить.
Представим, что вы хотите поместить аргумент макроса в строку. Язык С позволяет сделать это. Внутри заменяющей части функционального макроса символ # становится операцией препроцессора, которая преобразует лексемы в строки. Пусть х является параметром макроса, тогда #х — это имя параметра, преобразованное в строку "х". Такой процесс называется превращением в строку и демонс трируется в листинге 16.3.
Листинг 16.3. Программа subst.c

Препроцессор и библиотека С 671
Вывод выглядит следующим образом:
Квадрат у равен 25.
Квадрат 2+4 равен 36.
В первом вызове макроса #х заменяется строкой "у", а во втором вызове вместо #х подставляется "2 + 4". Конкатенация строк ANSI С затем объединяет эти строки с другими строками в операторе printf() для получения финальной строки. Например, первый вызов макроса дает следующий оператор:
printf("Квадрат " "у" " равен %d.\n",((у)*(у)));
После этого конкатенация объединяет три расположенные рядом строки в одну:
"Квадрат у равен %d.\n"
Средство слияния препроцессора: операция ##
Подобно #, операция ## может применяться в заменяющей части функционального макроса. Вдобавок она может использоваться в заменяющей части объектного макроса. Операция ## объединяет две лексемы в одну. Предположим, вы могли бы записать такое определение:
#define XNAME(n) х ## n Тогда макрос XNAME(4)
будет расширен следующим образом: х4
В листинге 16.4 этот и еще один макрос применяются для слияния лексем с помощью операции ##.
Листинг 16.4. Программа glue.с

Ниже показан вывод:
х 1 = 14 х2 = 20 хЗ = 30
Обратите внимание, что в макросе PRINT_XN() операция # используется для объединения строк, а операция ## — для объединения лексем в новый идентификатор.
672 глава 16
Макросы с переменным числом аргументов:
... И ___VA_ARGS_______
Некоторые функции, скажем, printf(), принимают переменное количество аргументов. Обсуждаемый ранее заголовочный файл stdvar.h предоставляет инструменты для создания определяемых пользователем функций с переменным числом аргументов. В С99/С11 то же самое сделано и для макросов.
Идея заключается в том, что последний аргумент в списке аргументов для определения макроса может быть троеточием. Если это так, то в заменяющей части может применяться предопределенный макрос VA_ARGS , который будет подставлен
вместо троеточия. Для примера рассмотрим следующее определение:
#define PR(...) printf(___VA_ARGS___)
Предположим, что в программе содержатся вызовы макроса вроде показанных ниже:
PR("Здравствуйте");
PR("вес = %d, доставка = $%.2f\n", wt, sp);
Для первого вызова __VA_ARGS__ расширяется в один аргумент:
"Здравствуйте"
Для второго вызова он расширяется в три аргумента:
"вес = %d, доставка = $%.2f\n", wt, sp
Таким образом, результирующий код выглядит так:
printf("Здравствуйте" );
printf("вес = %d, доставка = $%.2f\n", wt, sp);
В листинге 16.5 приведен более сложный пример, в котором используются конкатенация строк и операция #.
Листинг 16.5. Программа variadic.c

В первом вызове макроса X имеет значение 1, так что #Х становится "1". В результате получается следующее расширение:
print("Сообщение " "1" ": " "х = %g\n", х);
Затем осуществляется конкатенация четырех строк, сокращая вызов к такому виду:
print("Сообщение 1: х = %g\n", х);
Препроцессор и библиотека С 673
В итоге имеем показанный ниже вывод:
Сообщение 1: х = 48
Сообщение 2: х = 48.00, у = 6.9282
Не забывайте, что троеточие должно быть последним аргументом макроса; следующее определение является ошибочным:
Читать дальше