Первые две строки предсказуемы. Заметим, однако, что даже внутри двойных кавычек в определении PR
переменная замещается соответствующим аргументом. Все аргументы в этом определении замещаются.Третья строка представляет интерес:
она становится следующей строкой:
после первого этапа макрорасширения. Второе SQUARE(x)
расширяется, превращаясь в х*х, а первое остается без изменения, потому что теперь оно находится внутри двойных кавычек в операторе программы, и таким образом защищено от дальнейшего расширения. Окончательно строка программы содержити выводит на печать
при работе программы.
Давайте еще раз проверим то, что заключено в двойные кавычки. Если ваше макроопределение включает аргумент с двойными кавычками, то аргумент будет замещаться строкой из макровызова. Но после этого он в дальнейшем не расширяется, даже если строка является еще одним макроопределением. В нашем примере переменная х
стала макроопределением SQUARE(x) и осталась им.Теперь мы добрались до несколько специфических результатов. Вспомним, что х
имеет значение b. Это позволяет предположить, что SQUARE(x + 2) будет равно 6*6 или 36. Но напечатанный результат говорит, что получается число 14, которое, несомненно, никак не похоже на квадрат целого числа! Причина такого вводящего в заблуждение результата проста, и мы уже об этом говорили: препроцессор не делает вычислений, он только замещает строку. Всюду, где наше определение указывает на х, препроцессор подставит строку х + 2. Таким образом, х*х становится х + 2*х + 2.Единственное умножение здесь 2*x
. Если x равно 4, то получается следующее значение этого выражения: Этот пример точно показывает очень важное отличие между вызовом функции и макровызовом. Вызов функции передает
Можно ли ваше определение переделать так, чтобы SQUARE(x + 2)
было равно 36? Конечно. Нам просто нужно больше скобок:Тогда SQUARE(x + 2)
становится (х + 2)*(х + 2), и мы получаем наше желанное умножение, так как перенесли скобки в строку замещения.Однако это не решает всех наших проблем. Рассмотрим случаи, которые приводят к следующей строке на выходе: 100/SQUARE(2)
превращается в 100/2*2 .Вычисления следует вести слева направо, т. е. (100/2)*2
или 50*2 или 100.Эту путаницу можно исправить, определив SQUARE(x)
следующим образом:Это даст 100/(2 *2)
, что в конечном счете эквивалентно 100/4 или 25.Чтобы выполнить два последних примера, нам необходимо определение
Это урок использования необходимого количества скобок для гарантии, что операции и соединения выполняются в правильном порядке.
Даже эти предосторожности не спасают последний пример от беды: SQUARE(++ х)
превращается в ++х * ++х и x увеличивается дважды - один раз до умножения и один раз после: ++х * ++х = 5*6 = 30.(Так как порядок выполнения операций не установлен, то некоторые компиляторы превратят это в 6*5
, но конечный результат будет тем же самым.)Единственное лекарство в этом случае - не использовать ++х
в качестве аргумента для макроопределения. Заметим, что ++х обычноМАКРООПРЕДЕЛЕНИЕ ИЛИ ФУНКЦИЯ?
Многие задачи можно решать, используя макроопределение с аргументами или функцию. Что из них следует применять нам? На этот счет нет строгих правил, но есть некоторые соображения.
Макроопределения должны использоваться скорее как хитрости, а не как обычные функции: они могут иметь нежелательные побочные эффекты, если вы будете неосторожны. Некоторые компиляторы ограничивают макроопределения одной строкой, и, по-видимому, лучше соблюдать такое ограничение, даже если ваш компилятор этого не делает.