int a = -534;
unsigned int b = (unsigned int)a;
printf("%d, %d", a, b);
печать -534, -534
Почему преобразование типа не происходит?
Я ожидал, что это будет -534, 534
Если я изменяю код к
int a = -534;
unsigned int b = (unsigned int)a;
if(a < b)
printf("%d, %d", a, b);
не печать чего-либо..., в конце концов, a
меньше, чем b
??
Во-первых, вам не нужно приведение: значение a
неявно преобразуется в unsigned int
с присвоением b
. Итак, ваш оператор эквивалентен:
unsigned int b = a;
. Важным свойством целочисленных типов unsigned
в C и C ++ является то, что их значения всегда находятся в диапазоне [0, max ], где max для unsigned int
равно UINT_MAX
(определено в limits.h
). Если вы назначаете значение, не входящее в этот диапазон, оно преобразуется в этот диапазон. Итак, если значение отрицательное, вы добавляете UINT_MAX + 1
несколько раз, чтобы получить его в диапазоне [0, UINT_MAX
]. В приведенном выше коде мы как будто написали: unsigned int b = (UINT_MAX + a) + 1
. Это не равно -a
(534).
Обратите внимание, что вышесказанное верно независимо от того, является ли базовое представление дополнением до двух, дополнением до единиц или величиной знака (или любой другой экзотической кодировкой).Это можно увидеть примерно так:
signed char c = -1;
unsigned int u = c;
printf("%u\n", u);
assert(u == UINT_MAX);
На типичной машине с дополнением до двух с 4-байтовым int
, c
равно 0xff
и u
равно 0xffffffff
. Компилятор должен убедиться, что когда значение -1
присвоено u
, оно преобразуется в значение, равное UINT_MAX
.
Теперь вернемся к вашему коду, строка формата printf
неверна для b
. Вы должны использовать % u
. Когда вы это сделаете, вы обнаружите, что он печатает значение UINT_MAX - 534 + 1
вместо 534
.
При использовании в операторе сравнения <
, поскольку b
равно unsigned int
, a
также преобразуется в беззнаковый int
. Это, учитывая b = a
; ранее означает, что a ложно:
a
как unsigned int
равно b
.
Допустим, у вас есть машина с дополнением до единиц, и у вас есть:
signed char c = -1;
unsigned char uc = c;
Допустим, char
(подписанный или беззнаковый) на этой машине равен 8 битам. Тогда c
и uc
сохранят следующие значения и битовые шаблоны:
+----+------+-----------+
| c | -1 | 11111110 |
+----+------+-----------+
| uc | 255 | 11111111 |
+----+------+-----------+
Обратите внимание, что битовые шаблоны c
и uc
не то же самое. Компилятор должен убедиться, что c
имеет значение -1
, а uc
имеет значение UCHAR_MAX
, которое на этом компьютере равно 255. .
Более подробная информация о моем ответе на вопрос о SO .
Потому что вы используете % d
для печати. Используйте % u
для беззнакового значения. Поскольку printf - это функция vararg, она не может знать типы параметров и вместо этого должна полагаться на спецификаторы формата. Из-за этого приведение типа не имеет никакого эффекта.
Я полагаю, что на первый случай, почему b печатается как -534, Троник и Хасан ответили достаточно. Вы не должны использовать% d, а должны использовать% u.
Что касается вашего второго случая, снова будет происходить неявное приведение типов, и оба a и b будут одинаковыми, из-за чего ваше сравнение действительно дает ожидаемый результат.
По 2-му вопросу:
сравнение никогда не работает между двумя разными типами - они всегда неявно приводятся к «наименьшему общему знаменателю», которым в данном случае будет unsigned int
. Я знаю, что это мерзко и нелогично.
C иногда может быть уродливым зверем. Проблема в том, что -534 всегда представляет значение 0xfffffdea, независимо от того, хранится ли оно в переменной с типом unsigned int или signed int. Чтобы сравнить эти переменные, они должны быть одного типа, поэтому одна из них будет автоматически преобразована либо в беззнаковое, либо в подписанное int, чтобы соответствовать другому. Если они одного типа, они равны, поскольку представляют одно и то же значение.
Похоже, что желаемое поведение обеспечивается функцией abs:
int a = -534;
int b = abs(a);
printf("%d, %d", a, b);
Преобразование целочисленного типа из знакового в беззнаковый не изменяет битовую комбинацию, а просто изменяет интерпретацию битовой комбинации.
У вас также есть несоответствие спецификатора формата,% u следует использовать для целых чисел без знака, но даже тогда результат будет не 534, как вы ожидаете, а 4294966762.
Если вы хотите сделать отрицательное значение положительным, просто отрицать это:
unsigned b = (unsigned)-a ;
printf("%d, %u", a, b);
Что касается второго примера, операции между типами с разной подписью включают в себя тайные неявные правила преобразования - избегайте. Вы должны установить высокий уровень предупреждений вашего компилятора, чтобы перехватить многие из этих ошибок. Я предлагаю, например, / W4 / WX в VC ++ и -Wall -Werror -Wformat для GCC.
ваш спецификатор в printf просит printf напечатать целое число со знаком, поэтому лежащие в основе байты интерпретируются как целое число со знаком.
Вы должны указать, что хотите получить целое число без знака, используя % u
.
править: a == b
верно для сравнения, что является странным поведением, но совершенно верно. Вы не изменили базовые биты, вы только попросили компилятор обработать базовые биты определенным образом. Следовательно, побитовое сравнение дает истинное значение.
[предположение]
Я подозреваю, что поведение может варьироваться в зависимости от реализации компилятора - т.е. фиктивный ЦП может не использовать одну и ту же логику для обоих подписанных и беззнаковые числа, и в этом случае побитовое сравнение завершится ошибкой. [/ speculation]
Насколько я понимаю, if не работает, потому что компилятор предполагает, что вторую переменную следует считать того же типа, что и первая. Попробуйте if (b> a) , чтобы увидеть разницу.