Я пытаюсь понять, почему следующий код не выдает предупреждение в указанном месте.
//from limits.h
#define UINT_MAX 0xffffffff /* maximum unsigned int value */
#define INT_MAX 2147483647 /* maximum (signed) int value */
/* = 0x7fffffff */
int a = INT_MAX;
//_int64 a = INT_MAX; // makes all warnings go away
unsigned int b = UINT_MAX;
bool c = false;
if(a < b) // warning C4018: '<' : signed/unsigned mismatch
c = true;
if(a > b) // warning C4018: '<' : signed/unsigned mismatch
c = true;
if(a <= b) // warning C4018: '<' : signed/unsigned mismatch
c = true;
if(a >= b) // warning C4018: '<' : signed/unsigned mismatch
c = true;
if(a == b) // no warning <--- warning expected here
c = true;
if(((unsigned int)a) == b) // no warning (as expected)
c = true;
if(a == ((int)b)) // no warning (as expected)
c = true;
Я думал, что это связано с фоновым продвижением, но последние два, кажется, говорят иначе.
На мой взгляд, первое ==
сравнение - это такое же несоответствие знак / без знака, как и другие?
-1
прежнему будут работать (но они дают предупреждение), в то время как ваши сравнения с-1u
или(unsigned)-1
оба будут с треском провалиться.Ответы:
При сравнении подписанного с беззнаковым компилятор преобразует подписанное значение в беззнаковое. Для равенства это не имеет значения
-1 == (unsigned) -1
. Для других сравнений это имеет значение, например, верно следующее:-1 > 2U
.РЕДАКТИРОВАТЬ: Ссылки:
5/9: (Выражения)
4,7 / 2: (Интегральные преобразования)
EDIT2: уровни предупреждений MSVC
То, о чем предупреждают о различных уровнях предупреждений MSVC, конечно же, сделано разработчиками. Насколько я понимаю, их выбор в отношении равенства со знаком / без знака и сравнений больше / меньше имеет смысл, это, конечно, полностью субъективно:
-1 == -1
означает то же самое, что и-1 == (unsigned) -1
- я считаю, что это интуитивный результат.-1 < 2
не означает то же самое, что и-1 < (unsigned) 2
- На первый взгляд это менее интуитивно понятно, и ИМО заслуживает «более раннего» предупреждения.источник
(unsigned)-1
или-1u
часто хуже, чем по сравнению с-1
. Это потому что(unsigned __int64)-1 == -1
, но(unsigned __int64)-1 != (unsigned)-1
. Поэтому, если компилятор выдает предупреждение, вы пытаетесь отключить его, приводя к беззнаковому или используя,-1u
и если значение на самом деле оказывается 64-битным или вы случайно измените его на одно позже, вы сломаете свой код! И помните, чтоsize_t
это беззнаковый, 64-битный только на 64-битных платформах, и использование -1 для недопустимого значения очень часто с ним.Почему подписанные / неподписанные предупреждения важны и программисты должны уделять им внимание, демонстрирует следующий пример.
Угадайте вывод этого кода?
#include <iostream> int main() { int i = -1; unsigned int j = 1; if ( i < j ) std::cout << " i is less than j"; else std::cout << " i is greater than j"; return 0; }
Вывод:
Удивлен? Онлайн-демонстрация: http://www.ideone.com/5iCxY
Итог: для сравнения, если один операнд равен
unsigned
, то другой операнд неявно преобразуется в,unsigned
если его тип подписан!источник
i<0
. Тогдаi
меньше чемj
наверняка. Еслиi
не меньше нуля, тоì
его можно безопасно преобразовать в беззнаковый для сравненияj
. Конечно, сравнения подписанных и неподписанных будут медленнее, но их результат в некотором смысле будет более правильным.Оператор == просто выполняет побитовое сравнение (простым делением, чтобы узнать, равен ли он 0).
Сравнение меньше / больше зависит от знака числа.
4-битный пример:
1111 = 15? или -1?
так что если у вас 1111 <0001 ... это неоднозначно ...
но если у вас есть 1111 == 1111 ... Это то же самое, хотя вы этого не имели в виду.
источник
В системе, которая представляет значения с использованием двух дополнений (большинство современных процессоров), они равны даже в своей двоичной форме. Возможно, поэтому компилятор не жалуется на a == b .
И мне кажется странным, что компилятор не предупреждает вас об a == ((int) b) . Я думаю, это должно дать вам предупреждение об усечении целого числа или что-то в этом роде.
источник
Данная строка кода не генерирует предупреждение C4018, поскольку Microsoft использовала другой номер предупреждения (например, C4389 ) для обработки этого случая, а C4389 не включен по умолчанию (например, на уровне 3).
Из документации Microsoft для C4389:
// C4389.cpp // compile with: /W4 #pragma warning(default: 4389) int main() { int a = 9; unsigned int b = 10; if (a == b) // C4389 return 0; else return 0; };
Другие ответы довольно хорошо объяснили, почему Microsoft, возможно, решила сделать особый случай из оператора равенства, но я считаю, что эти ответы не очень полезны без упоминания C4389 или того, как включить его в Visual Studio .
Я также должен упомянуть, что если вы собираетесь включить C4389, вы также можете рассмотреть возможность включения C4388. К сожалению, официальной документации для C4388 нет, но, похоже, она появляется в следующих выражениях:
int a = 9; unsigned int b = 10; bool equal = (a == b); // C4388
источник