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-ce possible?
Cela fonctionne bien si je change la macro en:
#define INT32_MIN (-2147483648L)
Quelqu'un peut-il signaler le problème?
c
signed
numeric-limits
numeric-conversion
Jayesh Bhoi
la source
la source
CHAR_BIT * sizeof(int)
?-0x80000000
, mais faux pour-0x80000000L
,-2147483648
et-2147483648L
(gcc 4.1.2), la question est donc: pourquoi est l'int littéral-0x80000000
différent du littéral int-2147483648
?<limits.h>
définissentINT_MIN
comme(-2147483647 - 1)
, vous savez maintenant pourquoi.Réponses:
C'est assez subtil.
Chaque littéral entier de votre programme a un type. Son type est réglementé par un tableau du 6.4.4.1:
Si un nombre littéral ne peut pas entrer dans le
int
type 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 se passe comme suit:int
long
long long
.Les littéraux hexagonaux se comportent cependant différemment! Si le littéral ne peut pas entrer dans un type signé
int
, il essaiera d'abordunsigned int
avant de passer à des types plus grands. Voir la différence dans le tableau ci-dessus.Donc, sur un système 32 bits, votre littéral
0x80000000
est de typeunsigned 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 la valeur0x80000000
, une valeur positive.bal < INT32_MIN
invoque les conversions arithmétiques habituelles et le résultat de l'expression0x80000000
est promu deunsigned int
àlong long
. La valeur0x80000000
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 pasunsigned int
, mais essaie plutôt de l'adapter à l'intérieur d'unlong
. Le suffixe L indique également que vous en voulezlong
si possible . Le suffixe L a en fait des règles similaires si vous continuez à lire le tableau mentionné au 6.4.4.1: si le nombre ne rentre pas dans le demandélong
, ce qui n'est pas le cas dans le cas 32 bits, le compilateur vous donnera unlong long
où il conviendra très bien.la source
long
système2147483648L
, ne rentre pas dans unlong
, il devientlong long
, alors l'-
est appliqué - ce que je pensais.0x7FFFFFFF
. Essayez-le vous-même:#include <limits.h> printf("%X\n", INT_MAX);
0x7FFFFFFF
lorsqu'il est écrit en code source est toujours un nombre positif, mais votreint
variable peut bien sûr contenir des nombres binaires bruts jusqu'à la valeur 0xFFFFFFFF.ìnt n = 0x80000000
force une conversion du littéral non signé en un type signé. Ce qui se passera dépend de votre compilateur - c'est un comportement défini par l'implémentation. Dans ce cas, il a choisi d'afficher le littéral entier dans leint
, en écrasant le bit de signe. Sur d'autres systèmes, il peut ne pas être possible de représenter le type et vous invoquez un comportement indéfini - le programme peut se bloquer. Vous obtiendrez le même comportement si vous le faitesint n=2147483648;
, il n'est pas du tout lié à la notation hexadécimale.-
est appliqué aux entiers non signés pourrait être développée un peu. J'avais toujours supposé (bien que je ne me sois heureusement jamais appuyé sur l'hypothèse) que les valeurs non signées seraient "promues" en valeurs signées, ou peut-être que le résultat ne serait pas défini. (Honnêtement, cela devrait être une erreur de compilation; qu'est-ce que- 3u
cela signifie même?)0x80000000
est ununsigned
littéral avec une valeur 2147483648.Appliquer le signe moins unaire sur cela vous donne toujours un type non signé avec une valeur non nulle. (En fait, pour une valeur non nulle
x
, la valeur avec laquelle vous vous retrouvez estUINT_MAX - x + 1
.)la source
Ce littéral entier
0x80000000
a un typeunsigned int
.Selon la norme C (6.4.4.1 Constantes entières)
Et cette constante entière peut être représentée par le type de
unsigned int
.Donc, cette expression
-0x80000000
a le mêmeunsigned int
type. De plus, il a la même valeur0x80000000
dans la représentation du complément à deux qui calcule la manière suivanteCela a un effet secondaire si écrire par exemple
Le résultat sera à nouveau
INT_MIN
.Ainsi dans cet état
il est comparé
0
à une valeur non signée0x80000000
convertie en type long long int selon les règles des conversions arithmétiques habituelles.Il est évident que 0 est inférieur à
0x80000000
.la source
La constante numérique
0x80000000
est de typeunsigned int
. Si nous prenons-0x80000000
et faisons des maths complimentaires à ce sujet, nous obtenons ceci:Alors
-0x80000000 == 0x80000000
. Et comparer(0 < 0x80000000)
(car0x80000000
non signé) est vrai.la source
int
s 32 bits . Bien que ce soit un choix très courant, dans une implémentation donnée, elleint
peut être plus étroite ou plus large. Il s'agit toutefois d'une analyse correcte pour ce cas.-0x80000000
est une arithmétique non signée.~0x800000000
est un code différent.-0x80000000
! En fait, le complément de 2 n'a absolument rien à voir avec cette question.Un point de confusion survient lorsque l'
-
on pense que cela fait partie de la constante numérique.Dans le code ci-dessous se
0x80000000
trouve la constante numérique. Son type n'est déterminé que sur cela. Le-
est appliqué par la suite et ne change pas le type .Les constantes numériques brutes non décorées sont positives.
Si elle est décimal, le type attribué est premier type qui va le tenir:
int
,long
,long long
.Si la constante est octal ou hexadécimal, il obtient le premier type qui le tient:
int
,unsigned
,long
,unsigned long
,long long
,unsigned long long
.0x80000000
, sur le système OP obtient le type deunsigned
ouunsigned long
. Quoi qu'il en soit, il s'agit d'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 à along long
, les valeurs ne sont pas modifiées sur les 2 côtés de la comparaison, il en0 < INT32_MIN
va de même.Une autre définition évite ce comportement curieux
Laissez-nous marcher dans un pays fantastique pendant un certain temps où
int
etunsigned
sont 48 bits.Puis
0x80000000
rentre dansint
et est donc le typeint
.-0x80000000
est alors un nombre négatif et le résultat de l'impression est différent.[Retour au mot réel]
Étant donné qu'il
0x80000000
tient dans un type non signé avant un type signé car il est juste plus grandsome_signed_MAX
qu'encoresome_unsigned_MAX
, il s'agit d'un type non signé.la source
C a une règle selon laquelle le littéral entier peut être
signed
ouunsigned
dépend de s'il s'inscrit danssigned
ouunsigned
(promotion entière). Sur une32
machine à bits, le littéral0x80000000
seraunsigned
. Le complément de 2 se-0x80000000
trouve0x80000000
sur une machine 32 bits. Par conséquent, la comparaisonbal < INT32_MIN
est entresigned
etunsigned
et avant la comparaison selon la règle Cunsigned int
sera convertie enlong long
.C11: 6.3.1.8/1:
Par conséquent,
bal < INT32_MIN
c'est toujourstrue
.la source