Задачи по языку С

       

Чтобы понять эффект макроподстановки препроцессора,




int x=2;
PRINT( x*FIDGE(2) ); Чтобы понять эффект макроподстановки препроцессора, нужно провести ее в месте вызова
PR(a); putchar('\n') Всегда производится самая левая макроподстановка.

Сначала вызов заменяется строкой макроподстановки.
PR(x*FIDGE(2)); putchar('\n') Затем аргументы заменяются на соответствующие строки
printf("a = %d\t",(int)(a)) Опять производится самая левая макроподстановка, на этот раз PR.
printf("x*FIDGE(2) = %d\t", (int)( x*FIDGE(2))) Подставляются аргументы макроподстановки.
printf("x*FIDGE(2) = %d\t", (int)( x* k+3.14159)) Имя макроподстановки, встречающееся в строке, не заменяется, но аргументы макроподстановки, встречающиеся в строке подстановки, заменяться должны. Значит, a в макроподстановке PR заменяется на x*FIDGE(2), но FIDGE(2), встречающееся при задании формата печати в обращении к printf, не заменяется.
(int)( x* 2+3.14159) Заменяя формальный параметр на фактический, получаем неожиданный результат. Сначала умножаем, затем складываем и отбрасываем дробную часть.
Внимание! Макроподстановки могут быть источником трудно уловимых ошибок. Макроподстановка - это только замена одних строк на другие. Препроцессор ничего не знает о языке С. Поэтому многих неожиданных результатов можно избежать, если строго следовать нескольким правилам.

Правило 1. Заключайте в скобки строки-подстановки, если они содержат операции.

Нежелательного взаимодействия между строкой-подстановкой и контекстом в приведенной задаче не было бы, если бы FUDGE(k) определялось как (k+3.14159).



for( cel=0; cel
for( cel=0; cel Вначале производим макроподстановку PRINT2.
for( cel=0; cel Затем производим макроподстановку PR.
for( cel=0; cel Производим макроподстановку PRINT.
for( cel=0; cel Производим макроподстановку PR.
Обращение к PRINT2 выглядит как один оператор, но после макроподстановки появляются три. Только первое обращение к PR оказывается внутри цикла for. Второе обращение к PR происходит после выполнения цикла for со значением cel=150.

Правило 2. Не давайте расползаться макроподстановке; лучше использовать выражение, а не оператор, и не несколько операторов, а один-единственный.

В данной задаче, чтобы удовлетворить этому правилу, надо использовать в теле PRINT вместо точек с запятой запятые.



int x=1, y=2;
PRINT3( MAX(x++,y),x,y ); Макроподстановка PRINT3, конечно же, происходит прежде MAX. Однако, дабы не затемнять сути, в этой и последующей задачах подстановка PRINT не будет производиться. Тогда первый шаг состоит в подстановке строки вместо MAX.
(a<b ? b : a), x,y Затем заменяем аргументы макроподстановки на формальные.
(x++<y ? y : x++),x,y Наконец, производим вычисления.
(1<2 ? y : x++), и x=2 (y) 2
PRINT3( MAX(x++,y),x,y ); (x++<y ? y : x++),x,y (2<2 ? y : x++), и x=3 (x++) 3, и x=4 Теперь выполняем второе обращение к PRINT3.
При обращении x++ появляется только один раз, но в расширении фигурирует уже два раза, что приводит к увеличению x иногда на 1, иногда на 2. Тяжесть ответственности за защиту от таких нежелательных побочных эффектов можно возлагать или на того, кто определяет макроподстановку, или на того, кто ею пользуется.

Правило 3. Избегайте макроподстановок, которые могут привести к непредсказуемым или несообразным побочным эффектам.

Правило 3А. В обращениях к макроподстановкам избегайте выражений, содержащих побочные эффекты.

В общем случае проблема побочных эффектов достаточно хитрая. Следование правилу 3 часто означает копирование аргументов в локальные переменные подстановки; эта добавочная работа уменьшает преимущество (в скорости) макроподстановок перед обращением к функциям. Следование же правилу 3А требует знания того, какая подпрограмма реализована как макроподстановка, а какая как функция. В лучшем случае это нарушает представление о подпрограмме как некоей абстракции, а в худшем случае может привести к ошибке, если данная подпрограмма будет реализована по-другому.

В данной задаче следование правилу 3А не затронет саму MAX.


Содержание раздела