Spec-Zone.ru › GCC 6 CPP

3.10.4 Дублирование побочных эффектов

Многие программы на C определяют макрос min, для «минимума», так:

#define min(X, Y)  ((X) < (Y) ? (X) : (Y))

Когда вы используете этот макрос с аргументом, содержащим побочный эффект, как показано здесь,

next = min (x + y, foo (z));

он раскрывается следующим образом:

next = ((x + y) < (foo (z)) ? (x + y) : (foo (z)));

где x + y было заменено на X и foo (z) на Y.

Функция foo используется только один раз в операторе, как он представлен в программе, но выражение foo (z) было заменено дважды в развёртывании макроса. В результате foo может быть вызвана дважды при выполнении оператора. Если у неё есть побочные эффекты или вычисление занимает много времени, результаты могут не соответствовать вашим ожиданиям. Мы говорим, что min является небезопасным макросом.

Лучшее решение этой проблемы — определить min таким образом, чтобы вычислить значение foo (z) только один раз. Язык C не предоставляет стандартного способа сделать это, но это можно сделать с помощью расширений GNU следующим образом:

#define min(X, Y)                \
({ typeof (X) x_ = (X);          \
   typeof (Y) y_ = (Y);          \
   (x_ < y_) ? x_ : y_; })

Запись ‘({ … })’ создаёт составной оператор, который действует как выражение. Его значение — значение его последнего оператора. Это позволяет нам определить локальные переменные и присвоить каждому аргументу по одной. Локальные переменные имеют подчёркивание после их имён, чтобы уменьшить риск конфликта с идентификатором более широкого действия (полностью избежать этого невозможно). Теперь каждый аргумент вычисляется ровно один раз.

Если вы не хотите использовать расширения GNU C, единственным решением является осторожное использование макроса min. Например, можно вычислить значение foo (z), сохранить его в переменной и использовать эту переменную в min:

#define min(X, Y)  ((X) < (Y) ? (X) : (Y))
…
{
  int tem = foo (z);
  next = min (x + y, tem);
}

(где мы предполагаем, что foo возвращает тип int).

Далее: Самоссылочные макросы, Предыдущее: Поглощение точки с запятой, Вверх: Опасности использования макросов [Содержание][Индекс]

© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-6.5.0/cpp/Duplication-of-Side-Effects.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API