Confronti firmati / non firmati

Sto cercando di capire perché il codice seguente non emette un avviso nel luogo indicato.

//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: ' b) // warning C4018: '<' : signed/unsigned mismatch c = true; if(a <= b) // warning C4018: '= 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; 

Pensavo che avesse a che fare con la promozione dello sfondo, ma gli ultimi due sembrano dire il contrario.

A mio parere, il primo confronto == è tanto una mancata corrispondenza firmata / non firmata quanto le altre?

Quando si confronta firmato con unsigned, il compilatore converte il valore firmato in unsigned. Per l’uguaglianza, questo non importa, -1 == (unsigned) -1 . Per altri confronti è importante, ad esempio il seguente è vero: -1 > 2U .

EDIT: riferimenti:

5/9: (espressioni)

Molti operatori binari che si aspettano operandi di tipo aritmetico o di enumerazione causano conversioni e producono tipi di risultato in modo simile. Lo scopo è quello di produrre un tipo comune, che è anche il tipo del risultato. Questo modello è chiamato le solite conversioni aritmetiche, che sono definite come segue:

  • Se uno degli operandi è di tipo long double, l’altro deve essere convertito in long double.

  • Altrimenti, se uno degli operandi è doppio, l’altro deve essere convertito in doppio.

  • Altrimenti, se uno degli operandi è mobile, l’altro deve essere convertito in float.

  • In caso contrario, le promozioni integrali (4.5) devono essere eseguite su entrambi gli operandi.54)

  • Quindi, se uno degli operandi è senza firma l’altro deve essere convertito in unsigned long.

  • Altrimenti, se un operando è un long int e l’altro unsigned int, allora se un long int può rappresentare tutti i valori di un unsigned int, il unsigned int deve essere convertito in un long int; altrimenti entrambi gli operandi devono essere convertiti in unsigned long int.

  • Altrimenti, se uno degli operandi è lungo, l’altro deve essere convertito in long.

  • Altrimenti, se uno degli operandi non è firmato, l’altro deve essere convertito in non firmato.

4.7 / 2: (conversioni integrali)

Se il tipo di destinazione non è firmato, il valore risultante è il numero intero senza segno congruente al numero intero sorgente (modulo 2 n dove n è il numero di bit utilizzati per rappresentare il tipo senza segno). [Nota: nella rappresentazione a complemento a due, questa conversione è concettuale e non vi è alcun cambiamento nel modello di bit (se non vi è alcun troncamento). ]

EDIT2: Livelli di avviso MSVC

Ciò che viene messo in guardia sui diversi livelli di avviso di MSVC sono, ovviamente, le scelte fatte dagli sviluppatori. Per come la vedo io, le loro scelte in relazione all’uguaglianza firmata / non firmata rispetto al confronto maggiore / minore hanno senso, questo è del tutto soggettivo naturalmente:

-1 == -1 significa lo stesso di -1 == (unsigned) -1 – Trovo che un risultato intuitivo.

-1 < 2 non significa lo stesso di -1 < (unsigned) 2 - Questo è meno intuitivo a prima vista e IMO merita un avviso "precedente".

Perché gli avvisi firmati / non firmati sono importanti e i programmatori devono prestare attenzione a loro, è dimostrato dal seguente esempio.

Indovina l’output di questo codice?

 #include  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; } 

Produzione:

 i is greater than j 

Sorpreso? Demo online: http://www.ideone.com/5iCxY

Bottomline: in confronto, se un operando unsigned è unsigned , l'altro operando viene convertito implicitamente in unsigned se il suo tipo è firmato!

L’operatore == esegue solo un confronto bit a bit (per divisione semplice per vedere se è 0).

Il più piccolo / più grande dei confronti si basa molto più sul segno del numero.

4 bit Esempio:

1111 = 15? o -1?

quindi se hai 1111 <0001 ... è ambiguo ...

ma se hai 1111 == 1111 … è la stessa cosa anche se non volevi che fosse così.

In un sistema che rappresenta i valori utilizzando 2 complementi (i processori più moderni) sono uguali anche nella loro forma binaria. Questo potrebbe essere il motivo per cui il compilatore non si lamenta di un == b .

E per me è strano che il compilatore non ti avverta su a == ((int) b) . Penso che dovrebbe darti un avvertimento di troncamento intero o qualcosa del genere.