2014-09-29 12 views
7

Jestem zaskoczony zachowaniem C++, gdy stosuję bit-mądry, a nie unsigned char.C++ - Bit-mądry nie uchar produkuje int

Podaj wartość binarną 01010101b, która jest 0x55 lub 85. Zastosowanie bitowej wartości nie w ośmiobitowej reprezentacji powinno dać 10101010b, czyli 0xAA lub 170.

Jednak nie mogę powtórzyć powyższego w C++. Poniższy prosty asercja nie powiedzie się.

assert(static_cast<unsigned char>(0xAAu) == ~static_cast<unsigned char>(0x55u)); 

że wydrukowane Wartości 0x55, 0xAA i ~0x55 (jak uchar) ze kodu. I okazuje się, że ten bit nie robi tego, czego się spodziewam.

std::cout << "--> 0x55: " << 0x55u << ", 0xAA: " << 0xAAu << ", ~0x55: " 
    << static_cast<unsigned>(~static_cast<unsigned char>(0x55u)) << std::endl; 

--> 0x55: 85, 0xAA: 170, ~0x55: 4294967210 

Numer, który jest drukowany na ~0x55 jest równa 11111111111111111111111110101010b, który jest 32-bitowy bitową nie stanowi 0x55. Tak więc operator ~ działa na 32-bitowych liczbach całkowitych, nawet jeśli jednoznacznie rzutuje dane wejściowe na unsigned char. Dlaczego?

Zastosowałem inny test, aby zobaczyć, jaki typ zwraca operator ~. I okazuje się być int na wejściu unsigned char:

template <class T> 
struct Print; 

// inside main()  
Print<decltype(~static_cast<unsigned char>(0x55))> dummy; 

dała następujący błąd kompilatora, który wskazuje, że wynik jest typu int.

error: implicit instantiation of undefined template 'Print<int>' 
    Print<decltype(~static_cast<unsigned char>(0x55u))> dummy; 

Co robię źle? Lub, w jaki sposób uzyskać C++ do produkcji 0xAA z ~0x55?

Pełny kod jest here

Odpowiedz

9

Integral promocje są wykonywane na operand ~ możemy zobaczyć, przechodząc do sekcji draft C++ standard5.3.1Jednoargumentowy operatorom który mówi (podkr):

Operand ~ powinien mieć typ wyliczeniowy typu całkowego lub nieskalowany; wynik jest uzupełnieniem jego operandu. Wykonano całkowe promocje . typ wyniku jest typu promowanego argumentu [...]

i całkowe promocjach są opisane w sekcji 4.5Integral promocji mówiąc:

prvalue z typ całkowity inny niż bool, char16_t, char32_t lub wchar_t, którego całkowity stopień konwersji (4.13) jest mniejsza niż pozycja int może zostać przekonwertowana na wartość typu int, jeśli int może reprezentować wszystkie wartości typu źródłowego;

Dla kompletności, aby zobaczyć, że unsigned char ranga jest mniejsza niż rangi int możemy przejść do sekcji 4.13całkowita konwersja rankingu który mówi:

rangi Typ liczby całkowitej ze znakiem jest większy niż ranga dowolnego typu ze znakiem całkowitym o mniejszym rozmiarze.

oraz:

Rangę char równa rangę podpisanego char i unsigned char .

Jednym rozwiązaniem byłoby przypisać wynik do unsigned char których jest to bezpieczne, ponieważ nie trzeba się martwić o podpisaną całkowitej przepełnienie.

Jak podkreśla Ben Voigt, system byłby zgodny z sizeof (int) == 1 i CHAR_BIT >= 32. W takim przypadku ranga niepodpisanego znaku char nie będzie mniejsza niż int, a zatem promocja będzie niepodpisana. Nie znamy żadnych systemów, na których faktycznie się to odbywa.

+0

+1: Choć jest to prawdopodobnie oczywiste, warto wspomnieć, że 'unsigned char' jest rzeczywiście (a) dolna konwersji pozycja niż 'int', i (b) pełna reprezentacja jako' int' (wszystkie 'unsigned char' wartości mogą być reprezentowane jako' int'). Tak więc promocja jest dobra. – WhozCraig

+0

@WhozCraig, to sprawiedliwe żądanie, gotowe. –

+0

Dziękuję za szczegółową odpowiedź! – Lemming

1

Można rodzaju "obciąć" wiodące 1 poprzez nadanie wynik ~0x55 do unsigned char:

#include <iostream> 

int main() 
{ 
    unsigned char input = 0x55; 
    unsigned char output = ~input; 

    std::cout << "input: " << (int)input << " output: " << (int)output << std::endl; 

    return 0; 
} 

Wynik:

input: 85 output: 170 
+2

Nie trzeba wykonywać operacji '|', wystarczy przypisać do zmiennej 'unsigned char' powinno to zrobić:' output = ~ input'. –

+0

@MarkRansom Tak naprawdę! Dzięki. – TobiMcNamobi

2

Odpowiedź o integralnej promocji jest prawidłowe.

można uzyskać pożądane rezultaty poprzez odlewanie i Notting w odpowiedniej kolejności:

assert(static_cast<unsigned char>(0xAAu) == static_cast<unsigned char>(~0x55u));