Spec-Zone.ru › GCC 10 CPP

Next: Самоссылочные макросы, Previous: Поглощение точки с запятой, Up: Недостатки макросов [Оглавление][Индекс]

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).

Next: Самоссылочные макросы, Previous: Поглощение точки с запятой, Up: Недостатки макросов [Оглавление][Индекс]

© 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

Spec-Zone.ru

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