Spec-Zone.ru › GCC 5 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-5.5.0/cpp/Duplication-of-Side-Effects.html

Spec-Zone.ru

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