Estou tentando entender por que o código a seguir não emite um aviso no local 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;
Achei que tivesse a ver com promoção em segundo plano, mas os dois últimos parecem dizer o contrário.
Na minha opinião, a primeira ==
comparação é tanto uma incompatibilidade assinado / não assinado quanto as outras?
-1
ainda funcionarão (mas fornecem um aviso), enquanto suas comparações com-1u
ou(unsigned)-1
falharão miseravelmente.Respostas:
Ao comparar assinado com não assinado, o compilador converte o valor assinado em não assinado. Para igualdade, isso não importa
-1 == (unsigned) -1
. Para outras comparações é importante, por exemplo, o seguinte é verdadeiro:-1 > 2U
.EDIT: Referências:
5/9: (Expressões)
4.7 / 2: (conversões integrais)
EDIT2: Níveis de alerta MSVC
O que é alertado sobre os diferentes níveis de aviso do MSVC são, obviamente, escolhas feitas pelos desenvolvedores. A meu ver, suas escolhas em relação à igualdade assinado / não assinado vs comparações maiores / menores fazem sentido, isso é totalmente subjetivo, é claro:
-1 == -1
significa o mesmo que-1 == (unsigned) -1
- acho que é um resultado intuitivo.-1 < 2
não significa o mesmo que-1 < (unsigned) 2
- Isso é menos intuitivo à primeira vista e a IMO merece um aviso "mais cedo".fonte
(unsigned)-1
ou-1u
muitas vezes é pior do que comparar com-1
. Isso porque(unsigned __int64)-1 == -1
, mas(unsigned __int64)-1 != (unsigned)-1
. Portanto, se o compilador der um aviso, você tenta silenciá-lo convertendo em não assinado ou usando-1u
e se o valor realmente for de 64 bits ou você alterá-lo para um mais tarde, você quebrará seu código! E lembre-se de quesize_t
não tem sinal, 64 bits em plataformas de 64 bits apenas e usar -1 para valor inválido é muito comum com ele.Por que avisos assinados / não assinados são importantes e os programadores devem prestar atenção a eles, é demonstrado pelo exemplo a seguir.
Adivinhe a saída deste 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; }
Resultado:
Surpreso? Demonstração online: http://www.ideone.com/5iCxY
Resumindo: em comparação, se um operando for
unsigned
, o outro operando será convertido implicitamente emunsigned
se seu tipo for assinado!fonte
i<0
. Entãoi
é menor do quej
com certeza. Sei
não for menor que zero,ì
pode ser convertido com segurança para sem sinal para comparaçãoj
. Claro, as comparações entre com sinal e sem sinal seriam mais lentas, mas seu resultado seria mais correto em algum sentido.O operador == faz apenas uma comparação bit a bit (por divisão simples para ver se é 0).
Quanto menor / maior do que as comparações, depende muito mais do sinal do número.
Exemplo de 4 bits:
1111 = 15? ou -1?
então se você tem 1111 <0001 ... é ambíguo ...
mas se você tem 1111 == 1111 ... É a mesma coisa, embora você não quisesse que fosse.
fonte
Em um sistema que representa os valores usando 2-complemento (a maioria dos processadores modernos), eles são iguais mesmo em sua forma binária. Pode ser por isso que o compilador não reclama sobre a == b .
E para mim é estranho o compilador não avisá-lo sobre a == ((int) b) . Acho que deve dar a você um aviso de truncamento de número inteiro ou algo assim.
fonte
A linha de código em questão não gera um aviso C4018 porque a Microsoft usou um número de aviso diferente (ou seja, C4389 ) para lidar com esse caso e C4389 não está habilitado por padrão (ou seja, no nível 3).
Dos documentos da 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; };
As outras respostas explicaram muito bem por que a Microsoft pode ter decidido fazer um caso especial do operador de igualdade, mas acho que essas respostas não são muito úteis sem mencionar C4389 ou como habilitá-lo no Visual Studio .
Devo também mencionar que, se você pretende ativar o C4389, também pode considerar ativar o C4388. Infelizmente, não há documentação oficial para C4388, mas parece aparecer em expressões como as seguintes:
int a = 9; unsigned int b = 10; bool equal = (a == b); // C4388
fonte