Почему дополнение ведет себя по-разному через 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
превращается вint
1 тип до~
(побитовое дополнение) применяется оператор. Это из-за целое число акций, которые вызываются в операнд~
. В этом случае объектunsigned char
тип повышен до (подписано)int
, которое затем (после~
оценка оператора) используется описатель.обратите внимание, что по умолчанию аргумент акции (как
printf
является вариативной функцией) здесь не играет никакой роли, так как объект уже имеет типint
.С другой стороны, в этот код:
unsigned char c = 4, d; d = ~c; printf("%d", d);
следующие действия:
c
подлежит целое число акций из-за~
(точно так же, как описано выше)~c
rvalue 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.обратите внимание, что по умолчанию продвижение
char
c
до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);
чтобы получить тот же результат, что и в первом примере.
~
операнд подвергается целочисленному продвижению, и продвижение аргумента по умолчанию применяется к аргументу вариативных функций.