Почему 0



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



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


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

678   6  

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 тип, он попытается использовать следующий более крупный тип, как указано в приведенной выше таблице. Поэтому для обычных десятичных целочисленных литералов это выглядит так:

  • попробовать 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 типа unsigned int. Если мы возьмем -0x80000000 и сделать 2S комплимент математике на нем, мы получаем это:

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

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

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

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

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

Raw не украшенные числовые константы положительны.

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

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

0x80000000, в системе OP получает тип unsigned или unsigned long. В любом случае, это какой-то тип unsigned.

-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, это какой-то тип unsigned.

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

C11: 6.3.1.8 / 1:

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

таким образом, bal < INT32_MIN всегда true.

Comments

    Ничего не найдено.