Pourquoi est 0 < - 0x80000000?
J'ai ci-dessous un programme simple:
#include <stdio.h>
#define INT32_MIN (-0x80000000)
int main(void)
{
long long bal = 0;
if(bal < INT32_MIN )
{
printf("Failed!!!");
}
else
{
printf("Success!!!");
}
return 0;
}
La condition if(bal < INT32_MIN )
est toujours vraie. Comment est-il possible?
Cela fonctionne bien si je change la macro en:
#define INT32_MIN (-2147483648L)
Quelqu'un peut-il signaler le problème?
6 réponses
C'est assez subtil.
Chaque littéral entier de votre programme a un type. Le type qu'il a est réglementé par un tableau du 6.4.4.1:
Suffix Decimal Constant Octal or Hexadecimal Constant
none int int
long int unsigned int
long long int long int
unsigned long int
long long int
unsigned long long int
Si un nombre littéral ne peut pas tenir dans le type int
par défaut, il tentera le type plus grand suivant comme indiqué dans le tableau ci-dessus. Donc, pour les littéraux entiers décimaux réguliers, cela va comme:
- essayez
int
- si elle ne peut pas tenir, essayez
long
- si elle ne peut pas tenir, essayez
long long
.
Hex les littéraux se comportent différemment cependant! si le littéral ne peut pas tenir dans un type signé comme int
, Il va d'abord essayer unsigned int
avant de passer à essayer des types plus grands. Voir la différence dans le tableau ci-dessus.
, Donc sur un système 32 bits, votre littérale 0x80000000
est de type unsigned int
.
Cela signifie que vous pouvez appliquer l'opérateur unaire -
sur le littéral sans invoquer le comportement défini par l'implémentation, comme vous le feriez autrement en cas de débordement d'un entier signé. Au lieu de cela, vous obtiendrez le valeur 0x80000000
, une valeur positive.
bal < INT32_MIN
appelle les conversions arithmétiques habituelles et le résultat de l'expression 0x80000000
est promu de unsigned int
à long long
. La valeur 0x80000000
est conservée et 0 est inférieur à 0x80000000, d'où le résultat.
Lorsque vous remplacez le littéral par 2147483648L
vous utilisez la notation décimale et donc le compilateur ne choisit pas unsigned int
, mais essaie plutôt de l'insérer dans un long
. Aussi le L suffixe dit que vous voulez un long
si possible. Le suffixe L a en fait des règles similaires si vous continuez à lire la table mentionnée dans 6.4.4.1: si le nombre ne rentre pas dans le long
demandé, ce qui n'est pas le cas dans le cas 32 bits, le compilateur vous donnera un long long
où il ira très bien.
0x80000000
est un unsigned
littérale de la valeur 2147483648.
Appliquer le moins unaire sur ce Toujours Vous donne un type non signé avec une valeur non nulle. (En fait, pour une valeur non nulle x
, la valeur vous vous retrouvez avec UINT_MAX - x + 1
.)
Ce littéral entier 0x80000000
a le type unsigned int
.
Selon la norme C (6.4.4.1 constantes entières)
5 le type d'une constante entière est le premier de la constante correspondante liste dans laquelle sa valeur peut être représentée.
Et cette constante entière peut être représenté par le type de unsigned int
.
Donc cette expression
-0x80000000
a le même type unsigned int
. De plus il a la même valeur
0x80000000
dans la représentation du complément des deux qui calcule la manière suivante
-0x80000000 = ~0x80000000 + 1 => 0x7FFFFFFF + 1 => 0x80000000
Cela a un effet secondaire si écrire par exemple
int x = INT_MIN;
x = abs( x );
Le résultat sera à nouveau INT_MIN
.
Ainsi dans dans cette condition
bal < INT32_MIN
On compare 0
avec unsigned value 0x80000000
convertie en type long long int selon les règles des conversions arithmétiques habituelles.
, Il est évident que 0 est inférieure à 0x80000000
.
La constante numérique 0x80000000
est de type unsigned int
. Si nous prenons -0x80000000
et faisons 2S compliment math dessus, nous obtenons ceci:
~0x80000000 = 0x7FFFFFFF
0x7FFFFFFF + 1 = 0x80000000
, Donc -0x80000000 == 0x80000000
. Et comparer (0 < 0x80000000)
(puisque 0x80000000
n'est pas signé) est vrai.
Un point de confusion se produit lorsque l'on pense que -
fait partie de la constante numérique.
Dans le code ci-dessous 0x80000000
est la constante numérique. Son type est de déterminer seulement sur cela. Le -
est appliqué par la suite et ne change pas le type.
#define INT32_MIN (-0x80000000)
long long bal = 0;
if (bal < INT32_MIN )
Les constantes numériques brutes sont positives.
S'il est décimal, le type assigné est le premier type qui le contiendra: int
, long
, long long
.
Si la constante est octale ou hexadécimal, il obtient le premier type qui la tient: int
, unsigned
, long
, unsigned long
, long long
, unsigned long long
.
0x80000000
, sur le système OP obtient le type de unsigned
ou unsigned long
. De toute façon, c'est un type non signé.
-0x80000000
est également une valeur non nulle et étant un type non signé, il est supérieur à 0. Lorsque le code compare cela à un long long
, les valeurs ne sont pas modifiées sur les 2 côtés de la comparaison, donc 0 < INT32_MIN
est vrai.
Une autre définition évite ce curieux comportement
#define INT32_MIN (-2147483647 - 1)
Marchons dans la terre fantastique pendant un moment où int
et unsigned
sont 48 bits.
Alors 0x80000000
correspond à int
et le type int
l'est aussi. -0x80000000
est alors un nombre négatif et le résultat de l'impression est différente.
[retour au vrai-mot]
Puisque 0x80000000
correspond à un type non signé avant un type signé car il est juste plus grand que some_signed_MAX
mais dans some_unsigned_MAX
, c'est un type non signé.
C a une règle que le littéral entier peut être signed
ou unsigned
dépend si elle correspond à signed
ou unsigned
(promotion entière). Sur un 32
bits machine le littéral 0x80000000
sera unsigned
. Le complément de -0x80000000
de 2 est 0x80000000
sur une machine 32 bits. Par conséquent, la comparaison bal < INT32_MIN
est comprise entre signed
et unsigned
et avant la comparaison selon la règle C unsigned int
sera convertie en long long
.
C11: 6.3.1.8 / 1:
[...] Sinon, si le type de l'opérande avec un entier signé type permet de représenter l'ensemble des valeurs du type de l'opérande de type entier non signé, puis l'opérande de type entier non signé est convertie dans le type de l'opérande de type entier signé.
Par conséquent, bal < INT32_MIN
est toujours true
.