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

Spec-Zone.ru

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