Estoy tratando de entender por qué el siguiente código no emite una advertencia en el lugar indicado.
//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;
Pensé que tenía que ver con la promoción de fondo, pero los dos últimos parecen decir lo contrario.
En mi opinión, ¿la primera ==
comparación es tanto una falta de coincidencia firmada / no firmada como las demás?
-1
seguirán funcionando (pero dan una advertencia) mientras que sus comparaciones con-1u
o(unsigned)-1
ambos fallarán miserablemente.Respuestas:
Al comparar firmado con no firmado, el compilador convierte el valor firmado en no firmado. Para la igualdad, esto no importa
-1 == (unsigned) -1
. Para otras comparaciones que importe, por ejemplo, lo siguiente es cierto:-1 > 2U
.EDITAR: Referencias:
5/9: (Expresiones)
4.7 / 2: (Conversiones integrales)
EDIT2: niveles de advertencia de MSVC
Lo que se advierte sobre los diferentes niveles de advertencia de MSVC son, por supuesto, las decisiones tomadas por los desarrolladores. Como lo veo, sus elecciones en relación con la igualdad firmado / no firmado frente a comparaciones mayores / menores tienen sentido, esto es completamente subjetivo, por supuesto:
-1 == -1
significa lo mismo que-1 == (unsigned) -1
- Encuentro que es un resultado intuitivo.-1 < 2
no significa lo mismo que-1 < (unsigned) 2
- Esto es menos intuitivo a primera vista, y la OMI merece una advertencia "antes".fuente
(unsigned)-1
o-1u
es a menudo peor que comparar-1
. Eso es porque(unsigned __int64)-1 == -1
, pero(unsigned __int64)-1 != (unsigned)-1
. Entonces, si el compilador da una advertencia, intentas silenciarlo convirtiéndolo en unsigned o usando-1u
y si el valor realmente es de 64 bits o lo cambias a uno más tarde, ¡romperás tu código! Y recuerde quesize_t
no está firmado, es de 64 bits solo en plataformas de 64 bits y el uso de -1 para un valor no válido es muy común.Por qué las advertencias firmadas / no firmadas son importantes y los programadores deben prestarles atención, se demuestra en el siguiente ejemplo.
¿Adivina la salida de este código?
#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; }
Salida:
¿Sorprendido? Demostración en línea: http://www.ideone.com/5iCxY
En resumen: en comparación, si un operando es
unsigned
, entonces el otro operando se convierte implícitamente enunsigned
si su tipo está firmado.fuente
i<0
. Entoncesi
es más pequeño quej
seguro. Sii
no es menor que cero, entoncesì
se puede convertir con seguridad a unsigned para compararloj
. Claro, las comparaciones entre firmados y no firmados serían más lentas, pero su resultado sería más correcto en cierto sentido.El operador == solo hace una comparación bit a bit (por división simple para ver si es 0).
Las comparaciones de menor / mayor que dependen mucho más del signo del número.
Ejemplo de 4 bits:
1111 = 15? o -1?
así que si tienes 1111 <0001 ... es ambiguo ...
pero si tienes 1111 == 1111 ... Es lo mismo aunque no quisiste que fuera.
fuente
En un sistema que representa los valores usando 2 complementos (la mayoría de los procesadores modernos) son iguales incluso en su forma binaria. Esta puede ser la razón por la que el compilador no se queja de a == b .
Y para mí, es extraño que el compilador no te advierte sobre a == ((int) b) . Creo que debería darte una advertencia de truncamiento de enteros o algo así.
fuente
La línea de código en cuestión no genera una advertencia C4018 porque Microsoft ha utilizado un número de advertencia diferente (es decir, C4389 ) para manejar ese caso, y C4389 no está habilitado de forma predeterminada (es decir, en el nivel 3).
De los documentos de Microsoft para 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; };
Las otras respuestas han explicado bastante bien por qué Microsoft podría haber decidido hacer un caso especial del operador de igualdad, pero encuentro que esas respuestas no son muy útiles sin mencionar C4389 o cómo habilitarlo en Visual Studio .
También debo mencionar que si va a habilitar C4389, también podría considerar habilitar C4388. Desafortunadamente, no hay documentación oficial para C4388, pero parece aparecer en expresiones como las siguientes:
int a = 9; unsigned int b = 10; bool equal = (a == b); // C4388
fuente