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?

247
demandé sur Matheus Moreira 2015-12-09 18:31:05

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.

356
répondu Lundin 2016-09-03 09:21:40

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.)

25
répondu Bathsheba 2015-12-09 15:57:33

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.

23
répondu Vlad from Moscow 2015-12-09 15:58:06

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.

12
répondu dbush 2015-12-09 15:48:03

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é.

11
répondu chux 2015-12-09 16:53:02

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.

8
répondu haccks 2016-01-13 13:35:52