Урок 12.2. Опредлеление макрофункций

12.2. Опредлеление макрофункций

В программировании принято называть идентификатор, следующий после #define, макроопределением. В случае если этот идентификатор встречается в программе, он называется макровызовом, а замена препроцессором макроопределения на строку замещения называется макрорасширением.

Если непосредственно за идентификатором следует открывающая скобка, то препроцессор считает, что в программе определено макроопределение с аргументами (его также называют макрофункцией). Общая форма записи директивы #define:

                      #define <идент.1>(<идент.2>,...,<идент.N>) <строка подстановки>

 В этом случае <идентификатор2>,...,<идентификаторN> называются формальными параметрами макроопределения. Использование макроопределений очень просто и очень похоже на вызов функции. Чтобы использовать макроопределение, нужно указать его имя (<идентификатор1>), а затем в скобках – фактические параметры. Встретив где-либо в программе имя макроопределения с последующим списком фактических параметров, заключенным в скобки (макровызов), препроцессор заменяет его на <строку подстановки>. При этом все вхождения формальных параметров в <строку подстановки> заменяются соответствующими фактическими параметрами, исключая те, которые в <строке подстановки> заключены в кавычки. После этого, как и в случае макроопределения без параметров, строка вновь просматривается, и если в ней есть другие макроопределения, они также соответствующим образом заменяются (расширяются). И в этом случае, естественно, строки, заключенные в двойные кавычки, считаются "защищенными". Пример:

#define VOL(a,b,c) a*b*c
#define PRNT(x) printf("x равен %d \n",x)
void main()
{
   int x, y, z, v;
   x = 3;
   y = 17;
   z = 15;
   v = VOL(x,y,z);
   PRNT(v);
}

 После завершения работы препроцессора эта программа будет иметь вид:

void main(){
   int x, y, z, v;
   x = 3;
   y = 17;
   z = 15;
   v = x * y * z;
   printf("x равен %d \n", v);
}

При макрорасширении строки PRNT(v) формальный параметр заменился на фактический только во втором случае, так как в первом случае он "защищен" кавычками.

Обратите внимание на строку v=VOL(x,y,z);. После расширения она "превратилась" в строку v=x*y*z;. Следовательно, как мы уже отмечали, препроцессор не выполняет вычислений, а только замещает строку. Таким образом, если бы мы написали v=VOL(x+3,y,z+12);, мы бы получили v=x+3*y*z+12;. Поскольку приоритет операции умножения выше, чем операции сложения, сначала будет выполнено умножение, а потом сложение. Но очевидно, что макрофункция VOL написана для перемножения своих аргументов. Для того чтобы избежать подобных ситуаций, следует каждый формальный параметр макроопределения в строке подстановки заключать в скобки:

#define VOL(a,b,c) (a)*(b)*(c)

 Тогда если мы запишем v = VOL(x+3, y, z+12);, то после работы препроцессора получим v = (x+3)*(y)*(z+12); и при работе программы вычисления будут выполняться правильно. Кроме того, рекомендуется всю строку подстановки заключать в скобки во избежание ошибок, подобных следующей: h = 15/VOL(x+3, y, z+12); . После макрорасширения эта строка преобразуется в h = 15/(x+3)*(y)*(z+12);. Так как вычисления выполняются слева направо, то сначала выполнится деление, а затем частное будет умножено на y и на (z+12), хотя ясно, что сначала имелось в виду выполнить умножение, а затем деление. Заключив строку подстановки в скобки, мы сможем избежать и этой ошибки:

  #define VOL(a,b,c) ((a)*(b)*(c))

 В этом случае h = 15/VOL(x+3,y,z+12); после расширения будет иметь вид h = 15/((x+3)*(y)*(z+12)); и вычисления будут производиться правильно.

На этом примере видно очень важное отличие макроопределений от функций:

при вызове функций передается значение аргумента в функцию при выполнении программы, а макровызов передает строку аргументов при компиляции программы

(поэтому программист, используя макроопределения, не должен заботиться о совпадении типов формальных и фактических параметров. Ведь передается не значение, а строка.) Символ #, помещенный перед формальным параметром макроопределения в строке подстановки, указывает на необходимость преобразования его в строку. При макрорасширении #<формальный параметр> заменяется на "фактический параметр".

Если в приведенном выше примере программы вместо

#define PRNT(x) printf("x равен %d \n", x)
//написать
#define PRNT(x) printf(#x"равен %d \n", x),

то встретив в программе PRNT(v), препроцессор заменит его на printf("v""равен %d\n", v).

Довольно часто перед программистами встает вопрос: что использовать – макроопределение или функцию? Здесь можно привести следующие соображения:

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

Оставить комментарий

Ваш email не будет опубликован. Обязательные поля отмечены *

Вы можете использовать это HTMLтеги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Translate Переводчик

Подписка на новости

SmartResponder.ru
Ваш e-mail: *
Ваше имя: *

Хостинг для Wordpress сайтов