Подтвердить что ты не робот

Приведение типов к неподписанным в 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??

4b9b3361

Ответ 1

Во-первых, вам не нужен бросок: значение a неявно преобразуется в unsigned int с присвоением b. Таким образом, ваше утверждение эквивалентно:

unsigned int b = a;

Теперь важным свойством интегральных типов unsigned в 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);

На типичной машине с двумя четырьмя байтами int, c есть 0xff, а u - 0xffffffff. Компилятор должен убедиться, что, когда значение -1 присвоено u, оно преобразуется в значение, равное UINT_MAX.

Теперь, возвращаясь к вашему коду, строка формата printf неверна для b. Вы должны использовать %u. Когда вы это сделаете, вы обнаружите, что оно печатает значение UINT_MAX - 534 + 1 вместо 534.

При использовании в операторе сравнения <, так как b unsigned int, a также преобразуется в unsigned int. Это, учитывая b = a; ранее, означает, что a < b является ложным: a как unsigned int равно b.

Скажем, у вас есть машина для дополнений, и вы делаете:

signed char c = -1;
unsigned char uc = c;

Скажем, a char (подписанный или неподписанный) имеет 8 бит на этой машине. Затем c и uc будут хранить следующие значения и битовые шаблоны:

+----+------+-----------+
| c  |  -1  | 11111110  |
+----+------+-----------+
| uc | 255  | 11111111  |
+----+------+-----------+

Обратите внимание, что битовые шаблоны c и uc не совпадают. Компилятор должен убедиться, что c имеет значение -1, а uc имеет значение UCHAR_MAX, которое на этом компьютере равно 255.

Более подробная информация о ответе на вопрос здесь о SO.

Ответ 2

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

Ответ 3

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

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

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

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

Ответ 4

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

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

int a = -534;
int b = abs(a);
printf("%d, %d", a, b);

Ответ 5

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

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

Ответ 6

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

Ответ 7

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

Ответ 8

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

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

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

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

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