В дополнение к ответу @ Someprogrammerdude , здесь приведены соответствующие отрывки из Книги 1) :
Результат оператора ~ является побитовым дополнением его (повышен [ !! ] ) операнд (то есть каждый бит в результатеустанавливается в том и только в том случае, если соответствующий бит в преобразованном операнде не установлен). Целочисленные преобразования выполняются для операнда, и результат имеет продвинутый тип. Если продвигаемый тип является типом без знака, выражение ~ E эквивалентно максимальному значению, представляемому вэтот тип минус E .
Каждый целочисленный тип имеет ранг целочисленного преобразования, определенный следующим образом:
- Никакие два целочисленных типа со знаком не должны иметь одинаковый ранг, даже если они имеют одинаковое представление.
- Ранг целого типа со знаком должен быть больше ранга любого целочисленного типа со знаком с меньшей точностью.
- Ранг long long int должен быть больше, чем ранг long int , который должен быть больше ранга int , который должен быть больше ранга short int , который должен быть больше ранга подписанный символ .
- Ранг любого целого типа без знака должен равняться рангу соответствующего целочисленного типа со знаком, если таковой имеется.
- Ранг любого стандартного целочисленного типа должен быть больше ранга любогорасширенный целочисленный тип с той же шириной.
- Ранг char должен равняться рангу знакового символа и без знака char .
- Ранг _Bool должен быть меньше, чем ранг всех других стандартных целочисленных типов.
- Ранг любого перечисляемого типа должен равняться рангу совместимого целочисленного типа (см. 6.7.2.2).
- Ранг любого расширенного целочисленного типа со знаком относительно другого расширенного целочисленного типа со знаком с той же точностью определяется реализацией, но все же подчиняетсядругие правила определения ранга целочисленного преобразования.
- Для всех целочисленных типов T1 , T2 и T3 , если T1 имеет более высокий ранг, чем T2 and T2 имеет более высокий ранг, чем T3 , тогда T1 имеет более высокий ранг, чем T3 .
Следующее может использоваться в выражении везде, где может использоваться int или unsigned int :
- Объект или выражение сцелочисленный тип, чей ранг целочисленного преобразования меньше или равен рангу int и unsigned int .
- Битовое поле типа _Bool , int , подписано int или unsigned int . Если int может представлять все значения исходного типа, значение преобразуется в int ;в противном случае он преобразуется в без знака int .Они называются целочисленными продвижениями. 48) Все остальные типы не изменяются целочисленными продвижениями.
- Целочисленные продвижения сохраняют значение, включая знак.Как обсуждалось ранее, вопрос о том, рассматривается ли «простой» char как подписанный , определяется реализацией.
48) Применяются целочисленные повышениятолько: как часть обычных арифметических преобразований, в определенные выражения аргументов, в операнды унарных + , - и ~ операторов и обоим операндам операторов сдвига, указанным в соответствующих подпунктах.
Ваш вопрос:
Почему ~a
не возвращает unsigned char
, поскольку a
является unsigned char
?
Потому чтоприменяются целочисленные повышения.
unsigned char a = 3;
printf ("%d", ~a);
a
- это unsigned char
, тип с диапазоном, который может быть представлен int
.Так что a
получает звание int
.Предполагая, что 32-битная ширина int
с и дополняют два :
3 10 = 0000 0000 0000 0000 0000 0000 0000 0011
2 ~ 3 10 = 1111 1111 1111 1111 1111 1111 1111 1100
2 Результат, интерпретируемый как signed int
, является отрицательным, поскольку установлен старший значащий бит, знаковый бит.Преобразовать в десятичное число:1111 1111 1111 1111 1111 1111 1111 1100
2 ¬ 0000 0000 0000 0000 0000 0000 0000 0011
2 + 0000 0000 0000 0000 0000 0000 0000 0001
2 ────────────────────────────0000 0000 0000 0000 0000 0000 0000 0100
2
0100 2 = 0 × 2 3 + 1 × 2 2 + 0 × 2 2 + 0 ×2 2 = 1 × 2 2 = 4 10 = −4 10 (с оригинальным знаком)
~> printf()
печатает -4
.
Чтобы получить желаемый результат 252 с вашим исходным кодом, который использует "%d"
в качестве спецификатора формата, потребуется некоторое приведение:
unsigned char a = 3;
printf("%d\n", (int)((unsigned char) ~a)); // prints 252
// ^^^ ^^^^^^^^^^^^^
// | cast the result of ~a back to unsigned char *)
// | to discard the bits > CHAR_BIT
// cast the char back to int to agree with the format specifier
*) Благодаря Чуксу, который заставил меня вспомнить, что char
может быть signed
!Приведение к (возможно signed
) char
даст неверный результат -4.
Чтобы получить тот же результат без приведения, вы можете использовать модификатор длины hh
:
Модификаторы длины и их значения: чч Указывает, что следующие d , i , o , u , x или X спецификатор преобразования применяется к знаку char или unsigned char аргумент (аргумент будет повышен в соответствии с целочисленными повышениями, но его значение должно бытьпреобразуется в знаковый символ или неподписанный символ перед печатью);или что следующий указатель преобразования n применяется к указателю на аргумент со знаком char. [...]
unsigned char a = 3;
printf("%hhu\n", ~a); // prints 252
Проблема с другими вашими попытками:
printf ("%u", ~a);
Отображение: 4294967292
printf ("%hu", ~a);
Отображение: 65532
С~a
это int
eger, неправильный тип для спецификатора формата u
и
Если спецификация преобразования недопустима, поведение не определено. 248) Если какой-либо аргумент имеет неправильный тип для соответствующей спецификации конвертации, поведение не определено.
1) ISO / IEC 9899 / Cor3: 2007 aka C99: TR3 или C99