Почему дополнение ведет себя по-разному через printf?
Я читал главу о побитовых операторах, я наткнулся на программу оператора дополнения 1 и решил запустить ее на Visual C++.
int main ()
{
unsigned char c = 4, d;
d = ~c;
printf("%dn", d);
}
это дает действительный выход: 251
тогда вместо использования d в качестве переменной для хранения значения ~c, Я решил непосредственно напечатать значение ~c.
int main ()
{
unsigned char c=4;
printf("%dn", ~c);
}
это дает выход -5.
почему это не сработало?
6 ответов:
в этом заявлении:
printf("%d",~c);the
cпревращается вint1 тип до~(побитовое дополнение) применяется оператор. Это из-за целое число акций, которые вызываются в операнд~. В этом случае объектunsigned charтип повышен до (подписано)int, которое затем (после~оценка оператора) используется описатель.обратите внимание, что по умолчанию аргумент акции (как
printfявляется вариативной функцией) здесь не играет никакой роли, так как объект уже имеет типint.С другой стороны, в этот код:
unsigned char c = 4, d; d = ~c; printf("%d", d);следующие действия:
cподлежит целое число акций из-за~(точно так же, как описано выше)~crvalue is оценивается как (подписано)intзначение (например,-5)d=~cделает неявное преобразование изintдоunsigned char, аdесть такой тип. Вы можете думать об этом так же, какd = (unsigned char) ~c. Обратите внимание, чтоdне может быть отрицательным (это общее правило для всех беззнаковых типов).printf("%d", d);вызывает по умолчанию аргумент акции, таким образомdпревращается вintи (неотрицательное) значение сохраняется (т. е.intтип представляют все значенияunsigned charтип).
1) если предположить, что
intможет представлять все значенияunsigned char(см. Т. С. комментарий ниже), но это очень скорее всего, произойдет именно так. Более конкретно, мы предполагаем, чтоINT_MAX >= UCHAR_MAXдержит. Как правило,sizeof(int) > sizeof(unsigned char)держит и Байт состоит из восьми битов. В противном случаеcбудет преобразовано вunsigned int(как в подпункте C11 §6.3.1.1/p2), и спецификатор формата должен быть также изменены в соответствии с%uдля того чтобы избежать возникновения УБ (С11 §7.21.6.1/П9).
charпревращается вintнаprintfзаявление перед операцией~во втором фрагменте. Так чтоc, которая составляет0000 0100 (2's complement)в двоичном формате повышается до (предполагая 32-разрядную машину)
0000 0000 0000 0000 0000 0000 0000 0100 // Say it is xи его битовое дополнение равно дополнению двух значений минус один (
~x = −x − 1)1111 1111 1111 1111 1111 1111 1111 1011что это
-5в десятичной форме в виде дополнения 2.обратите внимание, что по умолчанию продвижение
charcдоintтакже выполняется вd = ~c;перед операцией дополнения, но результат преобразуется обратно в
unsigned charкакdтипаunsigned char.C11: 6.5.16.1 простое назначение (p2):
в простом задании (
=), значение правого операнда преобразуется в тип выражения присваивания и заменяет значение, хранящееся в объекте, обозначенном левым операндом.и
6.5.16 (p3):
тип выражения присваивания является тип левого операнда бы после преобразования значения.
чтобы понять поведение вашего кода, вам нужно изучить концепцию под названием 'Целое Число Акций' (это происходит в вашем коде неявно перед битовой мудрой не операцией на
unsigned charоперанд), как указано в проекте комитета N1570:§ 6.5.3.3 унарные арифметические операторы
- результат
~оператор побитового дополнения своего (повышенный) операнд (то есть каждый бит в результате устанавливается, если, и только если соответствующий бит в преобразованном операнде не установлен). в целочисленные акции выполняются на операнде, и результат имеет повышенный тип. Если повышенный тип является "беззнаковым типом", то выражение~Eэквивалентно максимальному значению, представленному в этом типа минусE"., потому что
unsigned charтип уже, чем (так как он требует меньше байтов)intтип, - неявный тип продвижение осуществляется абстрактной машиной (компилятором) и значением переменнойcпревращается вintв момент компиляции (до применения операции дополнения~). Это необходимо для правильного выполнения программы, потому что~нужен целочисленный операнд.§ 6.5 выражения
- некоторые операторы (унарный оператор
~, и бинарные операторы<<,>>,&,^и|, в совокупности описываются как побитовые операторы)требуются операнды, которые имеют целочисленный тип. Эти операторы дают значения, которые зависят от внутренних представлений целые числа и имеют определенные и неопределенные аспекты реализации для подписанных типов.компиляторы достаточно умны, чтобы анализировать выражения, проверять семантику выражений, выполнять проверку типов и арифметику преобразования, если требуется. Вот и причина, чтобы применить
~oncharтип нам не нужно явно писать~(int)c- вызывается явное приведение типов (и избежать ошибок).Примечание:
стоимостью
cпревращается вintв выражение~c, но типаcпо-прежнемуunsigned char- его типа нет. Не смущайтесь.важно: результат
~операцииintтип!, проверьте ниже код (у меня нет vs-компилятора, я использую gcc):#include<stdio.h> #include<stdlib.h> int main(void){ unsigned char c = 4; printf(" sizeof(int) = %zu,\n sizeof(unsigned char) = %zu", sizeof(int), sizeof(unsigned char)); printf("\n sizeof(~c) = %zu", sizeof(~c)); printf("\n"); return EXIT_SUCCESS; }скомпилируйте его и запустите:
$ gcc -std=gnu99 -Wall -pedantic x.c -o x $ ./x sizeof(int) = 4, sizeof(unsigned char) = 1 sizeof(~c) = 4обратите внимание: размер результата
~cэто то же самое, что иint, но не равноunsigned char- результат~оператор в этом выражении составляетint! это как уже упоминалось 6.5.3.3 унарные арифметические операторы
- результат унарного
-оператор является отрицательным от его (продвинутого) операнда. Целое акции выполняются на операнде, и результат имеет повышенный тип.теперь, как @haccks также объяснил в своем ответ -это результат
~cна 32-битной машине и для значенияc = 4- это:1111 1111 1111 1111 1111 1111 1111 1011в десятичном это
-5- это выход второй код!в своем первый код, еще одну строчку интересно понять
b = ~c;, потому чтоbэтоunsigned charпеременная и результат~cизintтип, так чтобы вместить значение результата~cдоbзначение результата (~c)вместились в беззнаковый тип char следующим образом:1111 1111 1111 1111 1111 1111 1111 1011 // -5 & 0xFF & 0000 0000 0000 0000 0000 0000 1111 1111 // - one byte ------------------------------------------- 1111 1011десятичный эквивалент
1111 1011и251. Вы могли бы получить тот же эффект использование:printf("\n ~c = %d", ~c & 0xFF);или как предложил @ouah в его ответ использование явного приведения.
при использовании
~операторcон получает повышение доint, в результатеintКак хорошо.затем
- в первом примере результат преобразуется в
unsigned charи затем повышен доsigned intи распечатать.- во 2-м примере результат печатается как
signed int.
это дает op -5. почему это не сработало?
вместо:
printf("%d",~c);использование:
printf("%d", (unsigned char) ~c);чтобы получить тот же результат, что и в первом примере.
~операнд подвергается целочисленному продвижению, и продвижение аргумента по умолчанию применяется к аргументу вариативных функций.