Почему неподписанный считается подписанным? - PullRequest
1 голос
/ 25 апреля 2020

Я знаю, что уже был очень похожий вопрос, на который уже дан ответ, но я считаю, что он не решает мою проблему.

unsigned char aaa = -10;
unsigned int bbb = (unsigned int)-5;
unsigned int ccc = (unsigned int)20 + (unsigned int)bbb;

printf("%d\n", aaa);
printf("%d\n", ccc);

Над отпечатками кода aaa = 246 (что я и ожидал), но ccc = 15 это означает, что unsigned int был полностью обработан как подписанный. Я не могу найти объяснения этому, даже пытаясь, вероятно, устареть приведения типов.

Ответы [ 2 ]

6 голосов
/ 25 апреля 2020

unsigned char aaa = -10;

Значение int -10 будет преобразовано в unsigned char путем многократного добавления UCHAR_MAX + 1, пока результат не будет в диапазоне [0, UCHAR_MAX] , Скорее всего, char имеет 8 бит в вашей системе, что означает UCHAR_MAX = 2**8 - 1 = 255. Так что -10 + UCHAR_MAX + 1 это 246, это в пределах досягаемости. Так aaa = 246.

unsigned int bbb = (unsigned int)-5;

-5 добавляется UINT_MAX + 1, при условии, что int имеет 32 бита, это приводит к bbb = 4294967291.

unsigned int ccc = (unsigned int)20 + (unsigned int)bbb;

Целочисленное переполнение без знака "обернуть вокруг". Таким образом, 20 + 4294967291 = 4294967311 больше, чем UINT_MAX = 2**32 - 1 = 4294967295. Поэтому мы вычитаем UINT_MAX+1, пока не окажемся в диапазоне [0, UINT_MAX]. Итак, 4294967311 - (UINT_MAX+1) = 15.

printf("%d\n", aaa);

Скорее всего, код в порядке. Скорее всего, на вашей платформе unsigned char повышается до int перед передачей в аргумент функции variadi c. Для получения информации о рекламных акциях вы можете прочитать cppreference неявных преобразований . Поскольку %d ожидает, что int и unsigned char повышены до int, код в порядке. [1]

printf("%d\n", ccc);

Эта строка приводит к неопределенному поведению . ccc имеет тип unsigned int, а спецификатор формата %d printf ожидает signed int. Поскольку в вашей платформе для представления чисел используется двоичное дополнение, в результате printf интерпретирует биты как значение со знаком, которое в любом случае равно 15.

[1]: теоретически существует возможность unsigned char имеет столько же битов, сколько int, поэтому unsigned char будет повышен до unsigned int вместо int, что также приведет к неопределенному поведению.

2 голосов
/ 25 апреля 2020

C типы переменных

C является языком программирования довольно низкого уровня, и он не сохраняет типы переменных после компиляции. Например, если переменные равны по размеру как без знака, так и без знака (скажем, uint64_t и int64_t), то после окончания компиляции каждый из них будет представлен как просто 8-байтовый фрагмент памяти. Все сложение / вычитание будут выполняться по модулю 2 в соответствующей степени (64 для 64-битных переменных и 32 для 32-битных). Единственное отличие, которое остается после компиляции, заключается в сравнении. Например, (без знака) -5> 1, но -5 <1. Я объясню, почему теперь </p>


Ваш -5 будет храниться по модулю 2 ^ 32, как и каждое 32-битное значение. -5 будет представлен как 0xfffffffb в фактической памяти. Алгоритм прост: если переменная подписана, то ее первый бит называется знаковым битом и указывает, положительный он или отрицательный. Первый бит 0xfffffffb равен 1, поэтому, когда дело доходит до сравнения со знаком, он отрицательный и меньше единицы. Но при сравнении как целое число без знака это значение на самом деле огромное, 2 ^ 32 - 5. Таким образом, в общем случае беззнаковое представление отрицательного числа со знаком больше на 2 ^ [количество битов в этом числе]. Вы можете прочитать больше об этой двоичной арифметике здесь . Итак, все, что произошло, вы получили число без знака, равное 0xfffffffb + 0x14 по модулю 2 ^ 32, что составляет 0x10000000f (мод 2 ^ 32), и это 0xf = 15.


In В заключение, "% d" предполагает, что аргумент подписан. Но это не главная причина, почему ответ произошел. Однако я советую использовать% u для чисел без знака.

...