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-10.5.0/cpp/Duplication-of-Side-Effects.html