Побочный эффект в аргументах к небезопасному макросу

Макрос содержит аргументы, которые могут быть оценены многократно или не оценены

Описание

Побочный эффект в аргументах к небезопасному макросу происходит, когда вы вызываете небезопасный макрос с выражением, которое имеет побочный эффект.

  • Небезопасный макрос: Когда расширено, небезопасный макрос оценивает свои аргументы многократно или не оценивает его аргумент вообще.

    Например, макрос ABS оценивает свой аргумент x дважды.

    #define ABS(x) (((x) < 0) ? -(x) : (x))

  • Побочный эффект: Когда оценено, выражение с побочным эффектом изменяет по крайней мере одну из переменных в выражении.

    Например, ++n изменяет n, но n+1 не изменяет n.

    Средство проверки не рассматривает побочных эффектов во вложенных макросах. Средство проверки также не рассматривает вызовы функции или энергозависимый переменный доступ как побочные эффекты.

Риск

Если вы вызываете небезопасный макрос с выражением, которое имеет побочный эффект, выражение выполнено многократно или не оценено вообще. Побочный эффект может произойти многократно или не произойти вообще, вызвав неожиданное поведение.

Например, в вызове MACRO(++n), вы ожидаете только один шаг переменной n. Если MACRO является небезопасным макросом, шаг происходит несколько раз или не происходит вообще.

Средство проверки отмечает выражения с помощью побочных эффектов в макросе assert, потому что макрос assert отключен в нережиме отладки. Чтобы скомпилировать в нережиме отладки, вы задаете макрос NDEBUG во время компиляции. Например, в GCC, вы используете флаг -DNDEBUG.

Фиксация

Выполните выражение с побочным эффектом в отдельном операторе, и затем используйте результат в качестве макро-аргумента.

Например, вместо:

MACRO(++n);
выполните операцию на двух шагах:
++n;
MACRO(n);
Также используйте подставляемую функцию вместо макроса. Передайте выражение с побочным эффектом в качестве аргумента к подставляемой функции.

Средство проверки считает модификации локальной переменной заданными только в области действия блока макро-тела как побочный эффект. Этого дефекта не может произойти, поскольку переменная видима только в макро-теле. Если вы видите дефект этого вида, игнорируете дефект.

Примеры

развернуть все

#define ABS(x) (((x) < 0) ? -(x) : (x))
  
void func(int n) {
  /* Validate that n is within the desired range */
  int m = ABS(++n);
 
  /* ... */
}

В этом примере макрос ABS оценивает свой аргумент дважды. Вторая оценка может привести к непреднамеренному шагу.

Исправление — отдельная оценка выражения от макро-использования

Одно возможное исправление должно сначала выполнить шаг, и затем передать результат макросу.

#define ABS(x) (((x) < 0) ? -(x) : (x))
  
void func(int n) {
  /* Validate that n is within the desired range */
  ++n;
  int m = ABS(n);
 
  /* ... */
}

Исправление — выполняет выражение в подставляемой функции

Другое возможное исправление должно выполнить выражение в подставляемой функции.

static inline int iabs(int x) {
  return (((x) < 0) ? -(x) : (x));
}
  
void func(int n) {
  /* Validate that n is within the desired range */
 
int m = iabs(++n);
 
  /* ... */
}

Информация о результате

Группа: Программирование
Язык: C | C++
Значение по умолчанию: 'off'
Синтаксис командной строки: SIDE_EFFECT_IN_UNSAFE_MACRO_ARG
Влияние: носитель

Введенный в R2018b