Почему 0 <-0x80000000?

У меня ниже простая программа:

#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;
}

Состояние if(bal < INT32_MIN ) всегда верно. Как это возможно?

Он отлично работает, если я изменю макрос на:

#define INT32_MIN        (-2147483648L)

Кто-нибудь может указать на проблему?

6 ответов

Решение

Это довольно тонко.

Каждый целочисленный литерал в вашей программе имеет тип. Какой тип у него регулируется таблицей в 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

Если буквенное число не может вписаться в значение по умолчанию int type, он попытается использовать следующий больший тип, как указано в таблице выше. Так что для обычных десятичных целочисленных литералов это выглядит так:

  • Пытаться int
  • Если это не подходит, попробуйте long
  • Если это не подходит, попробуйте long long,

Шестнадцатеричные литералы ведут себя иначе! Если литерал не может поместиться внутри подписанного типа, как int, сначала попробую unsigned int прежде чем перейти к попыткам больших типов. Смотрите разницу в таблице выше.

Так что в 32-битной системе ваш литерал 0x80000000 имеет тип unsigned int,

Это означает, что вы можете применить унарный - оператор для литерала без вызова поведения, определенного реализацией, как это было бы при переполнении целого числа со знаком. Вместо этого вы получите значение 0x80000000, положительное значение.

bal < INT32_MIN вызывает обычные арифметические преобразования и результат выражения 0x80000000 повышен с unsigned int в long long, Значение 0x80000000 сохраняется и 0 меньше 0x80000000, отсюда и результат.

Когда вы заменяете литерал с 2147483648L вы используете десятичную запись, и поэтому компилятор не выбирает unsigned int, а скорее пытается уместить его внутри long, Также суффикс L говорит, что вы хотите long если возможно. Суффикс L на самом деле имеет похожие правила, если вы продолжаете читать упомянутую таблицу в 6.4.4.1: если число не помещается в запрошенном longв 32-битном случае компилятор выдаст long long где это будет соответствовать просто отлично.

0x80000000 является unsigned литерал со значением 2147483648.

Применение унарного минуса к этому все еще дает вам тип без знака с ненулевым значением. (На самом деле, для ненулевого значения x, значение, которое вы в конечном итоге UINT_MAX - x + 1.)

Это целое число буквальное 0x80000000 имеет тип unsigned int,

Согласно стандарту C (6.4.4.1 Целочисленные константы)

5 Тип целочисленной константы является первым из соответствующего списка, в котором может быть представлено ее значение.

И эта целочисленная константа может быть представлена ​​типом unsigned int,

Так что это выражение

-0x80000000 имеет то же самое unsigned int тип. Более того, он имеет то же значение0x80000000 в представлении дополнения двух, который рассчитывает следующим образом

-0x80000000 = ~0x80000000 + 1 => 0x7FFFFFFF + 1 => 0x80000000

Это имеет побочный эффект, если написать, например,

int x = INT_MIN;
x = abs( x );

Результат будет снова INT_MIN,

Таким образом, в этом состоянии

bal < INT32_MIN

там сравнивается 0 с беззнаковым значением 0x80000000 преобразуется в тип long long int по правилам обычных арифметических преобразований.

Очевидно, что 0 меньше 0x80000000,

Путаница возникает в мышлении - является частью числовой константы.

В приведенном ниже коде 0x80000000 числовая константа Его тип определяется только по этому. - применяется позже и не меняет тип.

#define INT32_MIN        (-0x80000000)
long long bal = 0;
if (bal < INT32_MIN )

Сырые неукрашенные числовые константы являются положительными.

Если это десятичное число, то назначенный тип является первым типом, который будет содержать его: int, long, long long,

Если константа восьмеричная или шестнадцатеричная, она получает первый тип, который содержит ее: int, unsigned, long, unsigned long, long long, unsigned long long,

0x80000000, в системе ОП получает тип unsigned или же unsigned long, В любом случае, это какой-то неподписанный тип.

-0x80000000 также является некоторым ненулевым значением и является незнаковым типом, оно больше 0. Когда код сравнивает это с long longзначения не меняются на 2 сторонах сравнения, поэтому 0 < INT32_MIN правда.


Альтернативное определение избегает этого любопытного поведения

#define INT32_MIN        (-2147483647 - 1)

Давайте пойдем в сказочную страну, где int а также unsigned являются 48-битными.

затем 0x80000000 вписывается в int и так тип int, -0x80000000 затем отрицательное число, и результат распечатки отличается.

[Назад к реальному слову]

поскольку 0x80000000 вписывается в некоторый неподписанный тип перед подписанным типом, так как он больше, чем some_signed_MAX еще внутри some_unsigned_MAXЭто какой-то неподписанный тип.

Числовая константа 0x80000000 имеет тип unsigned int, Если мы возьмем -0x80000000 и сделать 2s комплимент на это, мы получим это:

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

Так -0x80000000 == 0x80000000, И сравнивая (0 < 0x80000000) (поскольку 0x80000000 без знака) верно.

C имеет правило, что целочисленный литерал может быть signed или же unsigned зависит от того, вписывается ли он в signed или же unsigned (целочисленное продвижение). На 32машина буквальная 0x80000000 будет unsigned, 2-е дополнение -0x80000000 является 0x80000000 на 32-битной машине. Поэтому сравнение bal < INT32_MIN находится между signed а также unsigned и перед сравнением согласно правилу C unsigned int будет преобразован в long long,

С11: 6.3.1.8/1:

[...] В противном случае, если тип операнда с целочисленным типом со знаком может представлять все значения типа операнда с целочисленным типом без знака, то операнд с целочисленным типом без знака преобразуется в тип операнда с целочисленный тип со знаком.

Следовательно, bal < INT32_MIN всегда true,

Другие вопросы по тегам