﻿# V1112\. Comparing expressions with different signedness can lead to unexpected results\.

Анализатор обнаружил подозрительное сравнение, в котором типы выражений имеют одинаковый ранг, но разные знаки\. При этом ранги типов меньше, чем у 'int'\. Из\-за неявного преобразования таких выражений к типу 'int' или 'unsigned int' сравнение может приводить к неожиданным результатам\. 

Рассмотрим синтетический пример:

```cpp
bool foo(char lhs, unsigned char rhs)
{
  return lhs == rhs; // <=
}
```

В примере присутствует сравнение переменных с типами разной знаковости: 'lhs' типа 'char' и 'rhs' типа 'unsigned char'\. Будем считать, что нижележащий тип 'char' — это 'signed char' \(например, на архитектуре x86\_64\)\. Тип 'unsigned char' может отразить диапазон значений от \[0 \.\. 255\], при этом тип 'char' – \[\-128 \.\. 127\]\. Согласно стандартам C и C\+\+, перед сравнением значений переменных должно произойти неявное преобразование типов \([integral promotion](https://en.cppreference.com/w/c/language/conversion#Integer_promotions)\), в результате которого и может возникнуть проблема\.

Компилятор превращает код со сравнением в следующий:

```cpp
return (int) lhs == (int) rhs;
```

Такое преобразование он делает, если тип 'int' может отобразить диапазон значений 'char' и 'unsigned char', иначе вместо 'int' выберется 'unsigned int'\. На большинстве современных платформ тип 'int' занимает 4 байта и с лёгкостью может отобразить эти диапазоны\.

В случае, если 'lhs' имел отрицательное значение, то оно же и сохранится в результате преобразования в левом операнде\. При этом значение правого операнда после преобразования 'rhs' всегда будет неотрицательным, т\.к\. исходный тип был беззнаковым\. В итоге результат сравнения будет вычисляться как 'false'\.** **Возможна и обратная ситуация\. Если в переменной 'rhs' находится значение в диапазоне \[128 \.\. 255\], то в этой ситуации результат сравнения будет также 'false'\.

Такая ошибка может неожиданно проявить себя при смене компилятора или настроек, когда ранее тип 'char' был [беззнаковым](https://godbolt.org/z/nq5xq4sWo), а стал [знаковым](https://godbolt.org/z/esraxqeov), и наоборот\. Например, в таком случае при вызове функции 'foo' с аргументами '\\xEE' и '\\xEE' будет считаться, что переданы неравные значения\. Хотя такое поведение логично с точки зрения стандарта, оно может быть неожиданным для разработчика\.

Ошибки можно избежать двумя способами\.

**Способ N1**\. Преобразовать выражения к общему типу по знаку:

```cpp
if ((unsigned char) lhs == rhs)
```

**Способ N2**\. Воспользоваться семейством функций '[std::cmp\_\*](https://en.cppreference.com/w/cpp/utility/intcmp)' \(С\+\+20\) или их аналогами для сравнения выражений, типы которых имеют различную знаковость:

```cpp
if (std::cmp_equal(lhs, rhs))
```

**Примечание**\. В диагностическом правиле реализованы несколько исключений, которые добавлены для уменьшения количества ложных срабатываний\. Анализатор выдаёт срабатывание лишь в том случае, если он смог доказать, что диапазон значений одного из операндов не может быть отображён типом другого операнда\. Если же требуется выявить все места в коде, где происходит такое сравнение операндов, разных по знаковости, можно воспользоваться следующим комментарием:

```cpp
//+V1112, ENABLE_ON_UNKNOWN_VALUES
```

По этой причине анализатор не выдаёт срабатывание на приведённом ранее синтетическом примере без этой настройки\.