Книга: Язык Си - руководство для начинающих

ИСПОЛЬЗОВАНИЕ АРГУМЕНТОВ С #define

ИСПОЛЬЗОВАНИЕ АРГУМЕНТОВ С #define

     Макроопределение с аргументами очень похоже на функцию, поскольку аргументы его заключены в скобки. Ниже приведено несколько примеров, иллюстрирующих, как определяется и используется такая "макрофункция". В некоторых примерах к тому же указаны возможные ловушки, поэтому читайте их внимательно.

 

/* макроопределение с аргументами */

#define SQUARE (х) х*х

#define PR(x) printf("x равен %d.n" ,x)

main( )

{

int x = 4;

int z;

z = SQUARE(x);

PR(z);

z = SQUARE(2);

PR(z);

PR(SQUARE(x));

PR(SQUARE(x + 2));

PR(100/SQUARE(2));

PR(SQUAREC(++x));

}

Всюду, где в вашей программе появляется макроопределение SQUARE(x), оно заменяется на х*х. В отличие от наших прежних примеров при использовании этого макроопределения мы можем совершенно свободно применять символы, отличные от x. В макроопределении 'x' замещается символом, использованным в макровызове программы. Поэтому макроопределение SQUARE(2) замещается на 2*2. Таким образом, x на самом деле действует как аргумент. Однако, как вы вскоре увидите, аргумент макроопределения не "работает" точно так же, как аргумент функции. Вот результаты выполнения программы. Обратите внимание, что некоторые ответы отличаются от тех, которые вы могли бы ожидать.

z paвнo 16.

z paвнo 4.

SQUARE(x) равно 16.

SQUARE(x + 2) равно 14.

100/SQUARE(2) равно 100.

SQUAREC(++ x) равно 30.

     Первые две строки предсказуемы. Заметим, однако, что даже внутри двойных кавычек в определении PR переменная замещается соответствующим аргументом. Все аргументы в этом определении замещаются.

Третья строка представляет интерес:

PR(SQUARE(x));

она становится следующей строкой:

printf("SQUARE(x) равно %d.n", SQUARE(x));

после первого этапа макрорасширения. Второе SQUARE(x) расширяется, превращаясь в х*х, а первое остается без изменения, потому что теперь оно находится внутри двойных кавычек в операторе программы, и таким образом защищено от дальнейшего расширения. Окончательно строка программы содержит

printf(" SQUARE(x) равно %d.n", x*x);

и выводит на печать

SQUARE(x) равно 16.

при работе программы.

     Давайте еще раз проверим то, что заключено в двойные кавычки. Если ваше макроопределение включает аргумент с двойными кавычками, то аргумент будет замещаться строкой из макровызова. Но после этого он в дальнейшем не расширяется, даже если строка является еще одним макроопределением. В нашем примере переменная х стала макроопределением SQUARE(x) и осталась им.

     Теперь мы добрались до несколько специфических результатов. Вспомним, что х имеет значение b. Это позволяет предположить, что SQUARE(x + 2) будет равно 6*6 или 36. Но напечатанный результат говорит, что получается число 14, которое, несомненно, никак не похоже на квадрат целого числа! Причина такого вводящего в заблуждение результата проста, и мы уже об этом говорили: препроцессор не делает вычислений, он только замещает строку. Всюду, где наше определение указывает на х, препроцессор подставит строку х + 2. Таким образом, х*х становится х + 2*х + 2.

Единственное умножение здесь 2*x. Если x равно 4, то получается следующее значение этого выражения:

4+2*4+2=4+8+2= 14.

     Этот пример точно показывает очень важное отличие между вызовом функции и макровызовом. Вызов функции передает значение аргумента в функцию во время выполнения программы. Макровызов передает строку аргументов в программу до ее компиляции; это другой процесс, происходящий в другое время.

     Можно ли ваше определение переделать так, чтобы SQUARE(x + 2) было равно 36? Конечно. Нам просто нужно больше скобок:

#define SQUARE(x) (x)*(x)

Тогда SQUARE(x + 2) становится (х + 2)*(х + 2), и мы получаем наше желанное умножение, так как перенесли скобки в строку замещения.

Однако это не решает всех наших проблем. Рассмотрим случаи, которые приводят к следующей строке на выходе: 100/SQUARE(2) превращается в 100/2*2 .

Вычисления следует вести слева направо, т. е. (100/2)*2 или 50*2 или 100.

Эту путаницу можно исправить, определив SQUARE(x) следующим образом:

#define SQUARE(x) (x*x)

Это даст 100/(2 *2), что в конечном счете эквивалентно 100/4 или 25.

     Чтобы выполнить два последних примера, нам необходимо определение

#define SQUARE(x) ((x)*(x))

     Это урок использования необходимого количества скобок для гарантии, что операции и соединения выполняются в правильном порядке.

Даже эти предосторожности не спасают последний пример от беды: SQUARE(++ х) превращается в ++х * ++х и x увеличивается дважды - один раз до умножения и один раз после: ++х * ++х = 5*6 = 30.

(Так как порядок выполнения операций не установлен, то некоторые компиляторы превратят это в 6*5, но конечный результат будет тем же самым.)

Единственное лекарство в этом случае - не использовать ++х в качестве аргумента для макроопределения. Заметим, что ++х обычно работает как аргумент функции, так как ему присваивается значение 5, и затем это значение 5 передается функции.

Оглавление книги


Генерация: 0.080. Запросов К БД/Cache: 0 / 0
поделиться
Вверх Вниз