web-dev-qa-db-fra.com

Pourquoi 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-ce possible?

Cela fonctionne bien si je change la macro en:

#define INT32_MIN        (-2147483648L)

Quelqu'un peut-il signaler le problème?

251
Jayesh Bhoi

C'est assez subtil.

Chaque littéral entier de votre programme a un type. Son type est réglementé par un tableau au 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 par défaut int, il tentera d'utiliser le type suivant le plus grand, comme indiqué dans le tableau ci-dessus. Donc, pour les littéraux entiers décimaux normaux, il se présente comme suit:

  • Essayez int
  • Si cela ne vous convient pas, essayez long
  • Si cela ne convient pas, essayez long long.

Les littéraux hexadécimaux se comportent cependant différemment! Si le littéral ne peut pas tenir dans un type signé comme int, il essaiera d'abord unsigned int avant de tenter d'essayer des types plus volumineux. Voir la différence dans le tableau ci-dessus.

Ainsi, sur un système 32 bits, votre littéral 0x80000000 est de type unsigned int.

Cela signifie que vous pouvez appliquer l'opérateur unaire - au littéral sans invoquer le comportement défini par l'implémentation, comme vous le feriez autrement en cas de dépassement d'un entier signé. Au lieu de cela, vous obtiendrez la valeur 0x80000000, une valeur positive.

bal < INT32_MIN invoque les conversions arithmétiques habituelles et le résultat de l'expression 0x80000000 passe de unsigned int à long long. La valeur 0x80000000 est préservé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. Par conséquent, le compilateur ne sélectionne pas unsigned int, mais tente plutôt de le faire rentrer dans un long. De plus, le suffixe L indique que vous souhaitez un longsi possible. Le suffixe L a en fait des règles similaires si vous continuez à lire la table mentionnée au 6.4.4.1: si le nombre n’entre pas dans le nom demandé long, ce qui n’est pas le cas dans le cas 32 bits, le compilateur vous donnera un long long où ça ira très bien.

359
Lundin

0x80000000 est un unsigned littéral avec la valeur 2147483648.

L'application du moins unaire sur ceci encore vous donne un type non signé avec une valeur non nulle. (En fait, pour une valeur différente de zéro, x, vous obtenez la valeur UINT_MAX - x + 1.)

26
Bathsheba

Ce littéral entier 0x80000000 a le type unsigned int.

Selon la norme C (6.4.4.1 Constantes de nombre entier)

5 Le type d'une constante entière est le premier de la liste correspondante dans laquelle sa valeur peut être représentée.

Et cette constante entière peut être représentée 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 à 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.

Donc dans cet état

bal < INT32_MIN

il est comparé 0 avec non signé valeur 0x80000000 converti en type long long int conformément aux règles des conversions arithmétiques habituelles.

Il est évident que 0 est inférieur à 0x80000000.

23
Vlad from Moscow

La constante numérique 0x80000000 est de type unsigned int. Si nous prenons -0x80000000 et que nous faisons un complément de maths, nous obtenons ceci:

~0x80000000 = 0x7FFFFFFF
0x7FFFFFFF + 1 = 0x80000000

Donc, -0x80000000 == 0x80000000. Et comparer (0 < 0x80000000) (puisque 0x80000000 n'est pas signé) est vrai.

12
dbush

Un point de confusion survient en pensant que le - fait partie de la constante numérique.

Dans le code ci-dessous, 0x80000000 est la constante numérique. Son type est déterminé uniquement 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 )

Raw les constantes numériques sans fioritures sont positives.

S'il est décimal, le type affecté est le premier type qui le conservera: int, long, long long.

Si la constante est octale ou hexadécimale, elle obtient le premier type qui la contient: int, unsigned, long, unsigned long, long long, unsigned long long.

0x80000000, sur le système de l'OP obtient le type unsigned ou unsigned long. De toute façon, il s'agit d'un type non signé.

-0x80000000 est également une valeur non nulle et 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 le 2 côtés de la comparaison, donc 0 < INT32_MIN est vrai.


Une autre définition évite ce comportement curieux

#define INT32_MIN        (-2147483647 - 1)

Marchons un peu dans un pays imaginaire où int et unsigned ont une résolution de 48 bits.

Alors 0x80000000 rentre dans int et il en est de même du type int. -0x80000000 est alors un nombre négatif et le résultat de l'impression est différent.

[Retour à real-Word]

Étant donné que 0x80000000 rentre dans un type non signé avant un type signé car il est juste supérieur à some_signed_MAX et pourtant dans some_unsigned_MAX, il s'agit d'un type non signé.

11
chux

C a une règle selon laquelle le littéral entier peut être signed ou unsigned, selon qu’il s’intègre dans signed ou unsigned (promotion d’entier). Sur une machine 32- bit, le littéral 0x80000000 sera unsigned. Le complément 2 de -0x80000000 est 0x80000000 sur un ordinateur 32 bits. Par conséquent, la comparaison bal < INT32_MIN se situe entre signed et unsigned et avant la comparaison, conformément à la règle C unsigned int sera convertie en long long.

C11: 6.3.1.8/1:

[...] Autrement, si le type de l'opérande avec le type entier signé peut représenter toutes les valeurs du type de l'opérande avec le type entier non signé, l'opérande avec le type entier non signé est converti en type de l'opérande avec type entier signé.

Par conséquent, bal < INT32_MIN est toujours true.

8
haccks