Поделиться через


Предупреждение компилятора (уровень 4) C4754

Правила преобразования для арифметических операций в сравнении означают, что одна ветвь не может быть выполнена.

Выводится предупреждение C4754, так как результат сравнения всегда совпадает. Это означает, что одна из ветвей условия никогда не выполняется, скорее всего, потому что связанное целочисленное выражение неверно. Этот дефект кода часто возникает в неправильных целочисленных проверках переполнения 64-разрядных архитектур.

Правила целочисленного преобразования сложны, и существует множество тонких ошибок. В качестве альтернативы исправлению каждого предупреждения C4754 можно обновить код для использования библиотеки SafeInt.

Примеры

В этом примере создается C4754:

// C4754a.cpp
// Compile with: /W4 /c
#include "errno.h"

int sum_overflow(unsigned long a, unsigned long b)
{
   unsigned long long x = a + b; // C4754

   if (x > 0xFFFFFFFF)
   {
      // never executes!
      return EOVERFLOW;
   }
   return 0;
}

Добавление a + b может привести к арифметическому переполнению, прежде чем результат переадресовывается до 64-разрядного значения и назначается 64-разрядной переменной x. Это означает, что проверка x является избыточной и никогда не перехватывает переполнение. В этом случае компилятор выдает это предупреждение:

Warning C4754: Conversion rules for arithmetic operations in the comparison at C4754a.cpp (7) mean that one branch cannot be executed. Cast '(a + ...)' to 'ULONG64' (or similar type of 8 bytes).

Чтобы устранить предупреждение, можно изменить оператор назначения, чтобы привести операнды к 8-байтным значениям:

// Casting one operand is sufficient to force all the operands in
// the addition be upcast according to C/C++ conversion rules, but
// casting both is clearer.
unsigned long long x =
   (unsigned long long)a + (unsigned long long)b;

Следующий пример также создает C4754.

// C4754b.cpp
// Compile with: /W4 /c
#include "errno.h"

int wrap_overflow(unsigned long a)
{
   if (a + sizeof(unsigned long) < a) // C4754
   {
      // never executes!
      return EOVERFLOW;
   }
   return 0;
}

Оператор sizeof() возвращает значение size_t, размер которого зависит от архитектуры. Пример кода работает на 32-разрядных архитектурах, где size_t используется 32-разрядный тип. Однако в 64-разрядных архитектурах size_t используется 64-разрядный тип. Правила преобразования целых чисел означают, что a переадресовываются до 64-разрядного значения в выражении a + b < a , как если бы оно было записано (size_t)a + (size_t)b < (size_t)a. Когда a и b являются 32-разрядными целыми числами, 64-разрядная операция сложения никогда не может переполнения, и ограничение никогда не держится. В результате код никогда не обнаруживает целочисленное состояние переполнения на 64-разрядных архитектурах. В этом примере компилятор выдает это предупреждение:

Warning C4754: Conversion rules for arithmetic operations in the comparison at C4754b.cpp (7) mean that one branch cannot be executed. Cast '4' to 'ULONG' (or similar type of 4 bytes).

Обратите внимание, что предупреждающее сообщение явно перечисляет константное значение 4 вместо исходной строки— к тому времени, когда анализ предупреждений обнаруживает код об ошибке, sizeof(unsigned long) уже был преобразован в константу. Поэтому может потребоваться отслеживать, какое выражение в исходном коде связано с константным значением в сообщении предупреждения. Наиболее распространенные источники кода, разрешенные для констант в сообщениях предупреждений C4754, являются выражениями, такими как sizeof(TYPE) и strlen(szConstantString).

В этом случае фиксированный код будет выглядеть следующим образом:

// Casting the result of sizeof() to unsigned long ensures
// that all the addition operands are 32-bit, so any overflow
// is detected by the check.
if (a + (unsigned long)sizeof(unsigned long) < a)

Обратите внимание , что номер строки, указанный в предупреждениях компилятора, является последней строкой инструкции. В сообщении предупреждения о сложном условном операторе, который распространяется по нескольким строкам, строка с дефектом кода может быть несколько строк до указанной строки. Например:

unsigned long a;

if (a + sizeof(unsigned long) < a || // incorrect check
    condition1() ||
    a == 0) {    // C4754 warning reported on this line
         // never executes!
         return INVALID_PARAMETER;
}