преобразование типа к неподписанному в C

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??

12
задан Lazer 2 March 2010 в 18:52
поделиться

8 ответов

Во-первых, вам не нужно приведение: значение 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 .

5
ответ дан 2 December 2019 в 06:08
поделиться

Потому что вы используете % d для печати. Используйте % u для беззнакового значения. Поскольку printf - это функция vararg, она не может знать типы параметров и вместо этого должна полагаться на спецификаторы формата. Из-за этого приведение типа не имеет никакого эффекта.

15
ответ дан 2 December 2019 в 06:08
поделиться

Я полагаю, что на первый случай, почему b печатается как -534, Троник и Хасан ответили достаточно. Вы не должны использовать% d, а должны использовать% u.

Что касается вашего второго случая, снова будет происходить неявное приведение типов, и оба a и b будут одинаковыми, из-за чего ваше сравнение действительно дает ожидаемый результат.

0
ответ дан 2 December 2019 в 06:08
поделиться

По 2-му вопросу: сравнение никогда не работает между двумя разными типами - они всегда неявно приводятся к «наименьшему общему знаменателю», которым в данном случае будет unsigned int . Я знаю, что это мерзко и нелогично.

0
ответ дан 2 December 2019 в 06:08
поделиться

C иногда может быть уродливым зверем. Проблема в том, что -534 всегда представляет значение 0xfffffdea, независимо от того, хранится ли оно в переменной с типом unsigned int или signed int. Чтобы сравнить эти переменные, они должны быть одного типа, поэтому одна из них будет автоматически преобразована либо в беззнаковое, либо в подписанное int, чтобы соответствовать другому. Если они одного типа, они равны, поскольку представляют одно и то же значение.

Похоже, что желаемое поведение обеспечивается функцией abs:

int a = -534;
int b = abs(a);
printf("%d, %d", a, b);
1
ответ дан 2 December 2019 в 06:08
поделиться

Преобразование целочисленного типа из знакового в беззнаковый не изменяет битовую комбинацию, а просто изменяет интерпретацию битовой комбинации.

У вас также есть несоответствие спецификатора формата,% u следует использовать для целых чисел без знака, но даже тогда результат будет не 534, как вы ожидаете, а 4294966762.

Если вы хотите сделать отрицательное значение положительным, просто отрицать это:

unsigned b = (unsigned)-a ;
printf("%d, %u", a, b);

Что касается второго примера, операции между типами с разной подписью включают в себя тайные неявные правила преобразования - избегайте. Вы должны установить высокий уровень предупреждений вашего компилятора, чтобы перехватить многие из этих ошибок. Я предлагаю, например, / W4 / WX в VC ++ и -Wall -Werror -Wformat для GCC.

0
ответ дан 2 December 2019 в 06:08
поделиться

ваш спецификатор в printf просит printf напечатать целое число со знаком, поэтому лежащие в основе байты интерпретируются как целое число со знаком.

Вы должны указать, что хотите получить целое число без знака, используя % u .

править: a == b верно для сравнения, что является странным поведением, но совершенно верно. Вы не изменили базовые биты, вы только попросили компилятор обработать базовые биты определенным образом. Следовательно, побитовое сравнение дает истинное значение.

[предположение] Я подозреваю, что поведение может варьироваться в зависимости от реализации компилятора - т.е. фиктивный ЦП может не использовать одну и ту же логику для обоих подписанных и беззнаковые числа, и в этом случае побитовое сравнение завершится ошибкой. [/ speculation]

4
ответ дан 2 December 2019 в 06:08
поделиться

Насколько я понимаю, if не работает, потому что компилятор предполагает, что вторую переменную следует считать того же типа, что и первая. Попробуйте if (b> a) , чтобы увидеть разницу.

0
ответ дан 2 December 2019 в 06:08
поделиться