У меня ниже простая программа:
#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)
Кто-нибудь может указать на проблему?
c
signed
numeric-limits
numeric-conversion
Джаеш Бхой
источник
источник
CHAR_BIT * sizeof(int)
?-0x80000000
, но неверно для-0x80000000L
,-2147483648
и-2147483648L
(GCC 4.1.2), поэтому возникает вопрос: почему это ИНТ буквальные-0x80000000
отличается от Int литерала-2147483648
?<limits.h>
определяютINT_MIN
как(-2147483647 - 1)
, теперь вы знаете почему.Ответы:
Это довольно тонко.
Каждый целочисленный литерал в вашей программе имеет тип. Какой тип он имеет, регулируется таблицей в 6.4.4.1:
Если литеральное число не может поместиться в
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
где он подойдет просто отлично.источник
long
системе2147483648L
, не будет соответствовать вlong
, поэтому она становитсяlong long
, то-
применяется - или так я думал.0x7FFFFFFF
. Попробуйте сами:#include <limits.h> printf("%X\n", INT_MAX);
0x7FFFFFFF
при написании в исходном коде всегда является положительным числом, но вашаint
переменная может, конечно, содержать необработанные двоичные числа вплоть до значения 0xFFFFFFFF.ìnt n = 0x80000000
преобразование литерала без знака в тип со знаком . Что произойдет, зависит от вашего компилятора - это поведение, определяемое реализацией. В этом случае он решил показать весь литерал вint
, перезаписывая бит знака. В других системах может быть невозможно представить тип, и вы вызываете неопределенное поведение - программа может аварийно завершить работу. Вы получите то же самое поведение, если вы сделаетеint n=2147483648;
это, оно вообще не связано с шестнадцатеричной нотацией.-
применяются к целым числам без знака, можно немного расширить. Я всегда предполагал (хотя, к счастью, никогда не полагался на предположение), что значения без знака будут «повышены» до значений со знаком, или, возможно, результат будет неопределенным. (Честно говоря, это должна быть ошибка компиляции; что вообще- 3u
значит?)0x80000000
являетсяunsigned
литералом со значением 2147483648.Применение унарного минуса к этому все еще дает вам тип без знака с ненулевым значением. (На самом деле, для ненулевого значения
x
значение, которое вы в итоге получите, равноUINT_MAX - x + 1
.)источник
Этот целочисленный литерал
0x80000000
имеет типunsigned int
.Согласно стандарту C (6.4.4.1 Целочисленные константы)
И эта целочисленная константа может быть представлена типом
unsigned int
.Так что это выражение
-0x80000000
имеет тот жеunsigned int
тип. Кроме того, он имеет то же значение0x80000000
в представлении дополнения двух, которое рассчитывает следующим образомЭто имеет побочный эффект, если написать, например,
Результат будет снова
INT_MIN
.Таким образом, в этом состоянии
там сравнивается
0
с беззнаковое значение0x80000000
преобразуется к типу INT долго долго в соответствии с правилами обычных арифметических преобразований.Очевидно, что 0 меньше, чем
0x80000000
.источник
Числовая константа
0x80000000
имеет типunsigned int
. Если мы возьмем-0x80000000
и сделаем 2s комплимент по математике, мы получим это:Так
-0x80000000 == 0x80000000
. И сравнение(0 < 0x80000000)
(так0x80000000
как без знака) верно.источник
int
с. Хотя это очень распространенный выбор, в любой конкретной реализации онint
может быть либо уже, либо шире. Однако это правильный анализ для этого случая.-0x80000000
арифметика без знака.~0x800000000
это другой код.-0x80000000
! На самом деле дополнение 2 не имеет никакого отношения к этому вопросу полностью.Когда мы думаем, что
-
это часть числовой константы, возникает путаница .В приведенном ниже коде
0x80000000
числовая константа. Его тип определяется только по этому.-
Применяется после и не меняет тип .Необработанные необработанные числовые константы являются положительными.
Если это десятичное, то тип присвоенный первый типа , который будет держать его:
int
,long
,long long
.Если константа восьмеричный или шестнадцатеричный, он получает первый тип , который держит его:
int
,unsigned
,long
,unsigned long
,long long
,unsigned long long
.0x80000000
, в системе ОП получает типunsigned
илиunsigned long
. В любом случае, это какой-то неподписанный тип.-0x80000000
также является некоторым ненулевым значением и является незнаковым типом, он больше 0. Когда код сравнивает это с along long
, значения не изменяются на 2 сторонах сравнения, так0 < INT32_MIN
что верно.Альтернативное определение избегает этого любопытного поведения
Давайте прогуляемся по фэнтезийной земле на некоторое время, где
int
иunsigned
48-битные.Тогда
0x80000000
вписываетсяint
и так типint
.-0x80000000
затем отрицательное число и результат распечатки отличается.[Назад к реальному слову]
Поскольку
0x80000000
вписывается в некоторый неподписанный тип перед подписанным типом, поскольку он просто больше, чемsome_signed_MAX
внутриsome_unsigned_MAX
, это некоторый неподписанный тип.источник
C имеет правило, что целочисленный литерал может быть
signed
илиunsigned
зависит от того, подходит ли онsigned
илиunsigned
(целочисленное продвижение). На32
-битной машине литерал0x80000000
будетunsigned
. 2-е дополнение-0x80000000
находится0x80000000
на 32-битной машине. Таким образом, сравнениеbal < INT32_MIN
междуsigned
иunsigned
и перед сравнением в соответствии с правилом Cunsigned int
будет преобразован вlong long
.С11: 6.3.1.8/1:
Поэтому
bal < INT32_MIN
всегда естьtrue
.источник