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

Spec-Zone.ru

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