exponenta event banner

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

Макрос содержит аргументы, которые можно вычислять несколько раз или не вычислять

Описание

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

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

    Например, 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++
По умолчанию: Откл.
Синтаксис командной строки: SIDE_EFFECT_IN_UNSAFE_MACRO_ARG
Воздействие: среднее
Представлен в R2018b