Почему дополнение ведет себя по-разному через 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 57

6 ответов:

в этом заявлении:

printf("%d",~c);

the c превращается в int1 тип до~ (побитовое дополнение) применяется оператор. Это из-за целое число акций, которые вызываются в операнд ~. В этом случае объект 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 унарные арифметические операторы

  1. результат ~ оператор побитового дополнения своего (повышенный) операнд (то есть каждый бит в результате устанавливается, если, и только если соответствующий бит в преобразованном операнде не установлен). в целочисленные акции выполняются на операнде, и результат имеет повышенный тип. Если повышенный тип является "беззнаковым типом", то выражение ~E эквивалентно максимальному значению, представленному в этом типа минус E".

, потому что unsigned char тип уже, чем (так как он требует меньше байтов)int тип, - неявный тип продвижение осуществляется абстрактной машиной (компилятором) и значением переменной c превращается в int в момент компиляции (до применения операции дополнения ~). Это необходимо для правильного выполнения программы, потому что ~ нужен целочисленный операнд.

§ 6.5 выражения

  1. некоторые операторы (унарный оператор ~, и бинарные операторы <<, >>,&,^ и |, в совокупности описываются как побитовые операторы)требуются операнды, которые имеют целочисленный тип. Эти операторы дают значения, которые зависят от внутренних представлений целые числа и имеют определенные и неопределенные аспекты реализации для подписанных типов.

компиляторы достаточно умны, чтобы анализировать выражения, проверять семантику выражений, выполнять проверку типов и арифметику преобразования, если требуется. Вот и причина, чтобы применить ~ on char тип нам не нужно явно писать ~(int)c - вызывается явное приведение типов (и избежать ошибок).

Примечание:

  1. стоимостью c превращается в int в выражение ~c, но типа c по-прежнему unsigned char - его типа нет. Не смущайтесь.

  2. важно: результат ~ операции 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 унарные арифметические операторы

    1. результат унарного - оператор является отрицательным от его (продвинутого) операнда. Целое акции выполняются на операнде, и результат имеет повышенный тип.

теперь, как @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);

чтобы получить тот же результат, что и в первом примере.

~ операнд подвергается целочисленному продвижению, и продвижение аргумента по умолчанию применяется к аргументу вариативных функций.

целочисленное продвижение, от стандарта:

Если тип операнда со знаком целочисленного типа может представлять все значений типа операнда с беззнаковый целочисленный тип, операнд с целочисленным типом без знака преобразуется в тип операнда со знаком целочисленного типа.