Почему 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)
может ли кто-нибудь указать на эту проблему?
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и перед сравнением согласно правилу Cunsigned intбудет преобразовано вlong long.C11: 6.3.1.8 / 1:
[...] В противном случае, если тип операнда со знаком целочисленного типа может представлять все значения типа операнда с беззнаковым целочисленным типом, то операнд с беззнаковым целочисленным типом преобразуется в тип операнда со знаком целочисленного типа.
таким образом,
bal < INT32_MINвсегдаtrue.
Comments