Странное поведение побитового NOT (~)

Как объяснить следующее поведение?

#include<iostream>

using namespace std;

int main(){

       unsigned char a = 8;

       cerr << "a: " << (int)a << '\n';

       unsigned char b = (~a) >> 6;

       cerr << "b: " << (int)b << '\n';

       unsigned char c = (~a);
       c = c >> 6;

       cerr << "c: " << (int)c << '\n';

       return 0;
}

Вывод:

a: 8
b: 255
c: 3

После дальнейшего тестирования кажется, что (~a) становится int, а не unsigned char. Вот почему 1 получает смещение.

Что происходит?

EDIT: Мой компилятор является стандартным gcc 4.1.2

Ответ 1

Все арифметические и побитовые операторы в C всегда расширяют свои аргументы, по крайней мере, до int, если они были изначально более короткими интегральными типами. Это то, как определяется язык. Спецификация языка называет это "интегральной рекламой".

(Основная причина этого заключается в том, чтобы упростить реализацию C на архитектурах, где аппаратное обеспечение не поддерживает эффективные операции в более коротких количествах, чем полное машинное слово. Конечно, это также частично потому, что оно всегда срабатывало таким образом и не может быть изменен без нарушения существующего кода, который зависит от этого поведения).

Ответ 2

~a = 0xFFFFFFF7, поэтому b = (~a) >> 6 приводит к b = 0xFF; В случае с имеем c = (~a);, приводящее к c = 0xF7, поэтому c>>6 будет 3. Henning Makholm объясняет целочисленное продвижение хорошо выше. Это статья также полезна.

Ответ 3

a = 8

~a = -9 (int)

~a >> 6 = -1 (int)

(unsigned char)-1 = 255