2014-09-29 13 views
7

Ich bin überrascht von C++ - Verhalten, wenn Bit-weise nicht auf ein vorzeichenloses Zeichen angewendet wird.C++ - Bit-weise nicht von uchar erzeugt int

Nehmen Sie den Binärwert 01010101b, der 0x55 oder 85 ist. Bitweises Anwenden auf eine Acht-Bit-Darstellung sollte 10101010b ergeben, was 0xAA oder 170 ist.

Allerdings kann ich das oben in C++ nicht reproduzieren. Die folgende einfache Assertion schlägt fehl.

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

I gedruckt, die Werte von 0x55, 0xAA und ~0x55 (als uchar) mit dem folgenden Code. Und es zeigt, dass das bitweise nicht tut, was ich von ihm erwarte.

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

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

Die Zahl, die für ~0x55 gedruckt wird, ist gleich 11111111111111111111111110101010b, die die 32-Bit-bitweise nicht von 0x55 ist. Der Operator ~ arbeitet also mit 32-Bit-Ganzzahlen, auch wenn ich die Eingabe explizit auf unsigned char umsetze. Warum das?

Ich wandte einen anderen Test an, um zu sehen, welchen Typ der ~ Operator zurückgibt. Und es stellt sich heraus int auf einem unsigned char eingegeben werden:

template <class T> 
struct Print; 

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

ergibt den folgenden Compiler-Fehler, was darauf hindeutet, dass das Ergebnis vom Typ int.

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

Was mache ich falsch? Oder, wie bekomme ich C++, 0xAA von ~0x55 zu produzieren?

Voll Code ist here

Antwort

9

Integral Aktionen auf den Operanden von ~ ausgeführt werden wir dies, indem Sie auf den draft C++ standard Abschnitt 5.3.1Unäre Operatoren sehen können, die sagen (Hervorhebung von mir):

Der Operand von ~ soll einen Ganzzahl- oder einen Uncoped-Aufzählungstyp haben; Das Ergebnis ist das eine Komplement seines Operanden. Integral Promotions werden durchgeführt. Der Typ des Ergebnisses ist die Art der beworbenen Operanden [...]

und die integralen Aktionen sind in Abschnitt 4.5Integral aktionen abgedeckt und zu sagen:

A prvalue eine Ganzzahliger Typ als bool, char16_t, char32_t oder wchar_t, dessen Ganzzahlumwandlungsrang (4.13) ist kleiner als der Rang von int kann in einen prvalue des Typs int konvertiert werden, wenn int alle die Werte des Quelltyps darstellen kann;

Für Vollständigkeit, zu sehen, dass unsigned char Rang kleiner ist als der Rang von int wir auf den Abschnitt 4.13Integer Umwandlung Rang gehen kann die sagt:

Der Rang eines Der vorzeichenbehaftete Integer-Typ muss größer sein als der Rang von jeder Ganzzahl-Typ mit Vorzeichen mit einer kleineren Größe.

und:

Der Rang char soll den Rang eines signed char und unsigned char gleich.

Eine Lösung wäre, um das Ergebnis zu einem unsigned char zuweisen die, das sicher ist, da Sie nicht über signierten Integer-Überlauf zu kümmern.

Wie Ben Voigt darauf hinweist, wäre es konform mit einem System, bei dem sizeof (int) == 1 und CHAR_BIT >= 32. In diesem Fall ist der Rang von unsigned char woudl nicht kleiner als int und daher würde die Promotion zu unsigned int führen. Wir kennen keine Systeme, auf denen dies tatsächlich auftritt.

+0

+1: Auch wenn es nahe liegt auf der Hand, ist es erwähnenswert, dass 'unsigned char' zwar (a) geringere Umwandlung Rang als "int" und (b) vollständig als "int" darstellbar (alle Werte ohne Vorzeichen können als "int" dargestellt werden). Die Werbung ist also solide. – WhozCraig

+0

@WhozCraig das ist eine faire Anfrage, fertig. –

+0

Vielen Dank für die ausführliche Antwort! – Lemming

1

Sie können Art "gestutzt" die führenden 1 des durch das Ergebnis der ~0x55 zu einer unsigned char Zuordnung:

#include <iostream> 

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

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

    return 0; 
} 

Ergebnis:

input: 85 output: 170 
+2

Es ist nicht notwendig, die '|' Operation zu tun, einfach zu einem 'unsigned char' Variable zuweisen sollte es tun:' output = ~ input'. –

+0

@MarkRansom So wahr! Vielen Dank. – TobiMcNamobi

2

Die Antwort über ganzheitliche Förderung ist richtig.

Sie können die gewünschten Ergebnisse durch Gießen und Notting in der richtigen Reihenfolge erhalten:

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