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

Spec-Zone.ru

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