Próbuję zrozumieć, dlaczego poniższy kod nie generuje ostrzeżenia we wskazanym miejscu.
//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;
Myślałem, że ma to związek z promocją w tle, ale dwie ostatnie wydają się mówić inaczej.
Moim zdaniem pierwsze ==
porównanie jest tak samo niedopasowaniem ze znakiem / bez znaku, jak inne?
-1
będą nadal działać (ale te ostrzegają), podczas gdy twoje porównania z-1u
lub(unsigned)-1
oba zakończą się żałośnie.Odpowiedzi:
Porównując podpisane z bez znaku, kompilator konwertuje podpisaną wartość na niepodpisaną. Dla równości to nie ma znaczenia
-1 == (unsigned) -1
. Dla innych porównań to ważne, na przykład poniższe nie jest prawdą-1 > 2U
.EDYCJA: Odnośniki:
5/9: (Wyrażenia)
4.7 / 2: (Integralne konwersje)
EDIT2: poziomy ostrzegawcze MSVC
To, o czym ostrzega się na różnych poziomach ostrzegawczych MSVC, to oczywiście wybory dokonane przez programistów. Jak widzę, ich wybory dotyczące równości ze znakiem / bez znaku w porównaniu z większymi / mniejszymi porównaniami mają sens, jest to oczywiście całkowicie subiektywne:
-1 == -1
oznacza to samo co-1 == (unsigned) -1
- uważam, że wynik jest intuicyjny.-1 < 2
to nie to samo, co-1 < (unsigned) 2
- Na pierwszy rzut oka jest to mniej intuicyjne, a IMO zasługuje na „wcześniejsze” ostrzeżenie.źródło
(unsigned)-1
lub-1u
często jest gorsze niż porównywanie-1
. To dlatego(unsigned __int64)-1 == -1
, ale(unsigned __int64)-1 != (unsigned)-1
. Więc jeśli kompilator wyświetli ostrzeżenie, spróbujesz go wyciszyć przez rzutowanie na niepodpisane lub użycie,-1u
a jeśli faktycznie zdarzy się, że wartość jest 64-bitowa lub zmienisz ją później na jedną, złamiesz swój kod! I pamiętaj, żesize_t
jest to wersja bez znaku, 64-bitowa tylko na platformach 64-bitowych, a używanie -1 dla nieprawidłowej wartości jest bardzo częste.Dlaczego podpisane / niepodpisane ostrzeżenia są ważne i programiści muszą zwracać na nie uwagę, przedstawia poniższy przykład.
Zgadnij wynik tego kodu?
#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; }
Wynik:
Zaskoczony? Demo online: http://www.ideone.com/5iCxY
Dolna linia: dla porównania, jeśli jeden operand jest
unsigned
, to drugi operand jest niejawnie konwertowany na,unsigned
jeśli jego typ jest podpisany!źródło
i<0
. Wtedyi
jest mniejszy niżj
na pewno. Jeślii
jest nie mniejsza niż zero,ì
można ją bezpiecznie przekonwertować na bez znaku, aby ją porównaćj
. Jasne, porównania między znakiem i bez znaku byłyby wolniejsze, ale ich wynik byłby w pewnym sensie bardziej poprawny.Operator == wykonuje po prostu bitowe porównanie (przez proste dzielenie, aby sprawdzić, czy wynosi 0).
Mniejsze / większe niż porównania opierają się znacznie bardziej na znaku liczby.
4-bitowy przykład:
1111 = 15? lub -1?
więc jeśli masz 1111 <0001 ... to niejednoznaczne ...
ale jeśli masz 1111 == 1111 ... To jest to samo, chociaż nie chciałeś, żeby tak było.
źródło
W systemie, który reprezentuje wartości przy użyciu dopełnienia 2 (większość nowoczesnych procesorów), są one równe nawet w postaci binarnej. Może dlatego kompilator nie narzeka na a == b .
A dla mnie dziwne, że kompilator nie ostrzega o a == ((int) b) . Myślę, że powinno dać ci ostrzeżenie o obcięciu liczby całkowitej lub coś w tym rodzaju.
źródło
Podany wiersz kodu nie generuje ostrzeżenia C4018, ponieważ firma Microsoft użyła innego numeru ostrzeżenia (tj. C4389 ) do obsługi tego przypadku, a C4389 nie jest domyślnie włączona (tj. Na poziomie 3).
Z dokumentów firmy Microsoft dotyczących 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; };
Inne odpowiedzi całkiem dobrze wyjaśniły, dlaczego Microsoft mógł zdecydować się na specjalny przypadek z operatora równości, ale uważam, że te odpowiedzi nie są zbyt pomocne bez wspominania o C4389 lub o tym, jak włączyć to w Visual Studio .
Powinienem również wspomnieć, że jeśli zamierzasz włączyć C4389, możesz również rozważyć włączenie C4388. Niestety nie ma oficjalnej dokumentacji dla C4388, ale wydaje się, że pojawia się w wyrażeniach takich jak następujące:
int a = 9; unsigned int b = 10; bool equal = (a == b); // C4388
źródło