C++ & g++ unsigned int in uint8_t?
Meine Idee war Bitshiften
unsigned int x = 24;
uint8_t y = x << 24;
cout << "Ausgabe" << y << endl;
if (y == 24) {
cout << "GOT IT!!!";
}
Leider bringt das hier nur schlechte Laune. unsigned int ist 4 bytes ( 32 bit) groß auf meiner Plattform und uint8 ist ein unsigned char, mit 8 bit. Also shifte ich um 24 nach links denn:
in x steht sowas wie 00000... und ganz viele Nullen... 00011000
so 00011000 ist meine 24 und wenn ich das wieder rum um 24 stellen nach links verschiebe, krallt sich doch uint8_t die ersten 8 Bit oder nicht, und dann sollte es passen?
Also so erkläre ich mir das, vielleicht krallt uint8_t sich auch die letzten 8 bit von meinem int, dann shifte ich halt nach rechts. Ob ich nach rechts oder nach links shifte, das gewünschte Ergebnis erreiche ich nicht.
GOT IT!!! kommt also nie.
Wie das dynamisch zu lösen ist, mit sizeof und so, kriege ich selber hin. Mir gehts jetzt erstmal ums Prinzip.
Die Frage ist wieder ultra dumm. Er krallt sich die letzten 8, dann muss ich gar nicht nach rechts shiften, ich muss überhaupt nicht shiften, passt scho.
Und bei der Ausgabe kommen nur kryptische Zeichen, weils als char interpretiert wird.
4 Antworten
Ich glaube, du verstehst grundlegend falsch, dass es zwischen der Repräsentation von Integern in C++ und der Repräsentation auf Hardware-Ebene einen Unterschied gibt.
Im Folgenden nutze ich keine Binär- sondern Hex-Zahlen, weil die Platzsparender sind!
Wenn du ...
uint32_t x = 0x0F;
... schreibst, wird das in C++ so repräsentiert:
0x00'00'00'0F
Auf Hardware-Ebene hängt es von der Bytereihenfolge ab, und kann Big-Endian, Little-Endian oder eine von zwei Middle-Endian-Varianten sein.
Bei modernen Intel-CPUs, die alle Little-Endian nutzen, sähe das dann so aus:
0x0F'00'00'00
Aber DENNOCH wird es von C++ ganz normal wie ...
0x00'00'00'0F
... behandelt!
Merke: Es ist völlig irrelevant, wie Integer im Speicher auf Hardware-Ebene dargestellt werden! Für C++ beginnen die niederwertigsten Bits IMMER rechts und enden mit den höherwertigsten IMMER links!
Wenn du jetzt also ...
uint32_t x = 0x0F;
... schreibst, ist das identisch, als wenn du ...
uint32_t x = 0x00'00'00'0F;
... schreiben würdest.
Und wenn du einfach nur ...
uint8_t y = x;
... schreibst, werden die linkesten 3 Byte weggeworfen, und nur das rechteste Byte beibehalten. Und zwar ist damit die C++ Repräsentation gemeint!
Nochmal: Es ist völlig Wurst, was auf Hardware-Ebene passiert!
uint32_t a = 0x12345678;
uint16_t b = a; // 0x5678
uint8_t c = a; // 0x78
Wenn du shiften würdest, würde das hier passieren:
uint32_t x = 0x12345678;
uint32_t y = x << 24;
// y == 0x78000000
Bei C++ gibt es übrigens zwei verschiedene Casting-Typen, um die C++ Räpresentation und die Hardware-Repräsentation unterscheiden zu können:
uint32_t x = 0x12345678;
auto a = static_cast<uint8_t>(x);
// a == 0x78
auto b = *reinterpret_cast<uint8_t *>(x);
// b = 0x12
// ... oder ...
// b = 0x78
Im letzteren Falle hängt das Ergebnis von der verwendeten CPU ab (Intel oder ARM) und kann sogar auch völlig anders aussehen!
Disclaimer: Ja, ich weiß, dass der reinterpret-Cast in diesem Falle abhängig von der Hardware UB ist!
Fazit: Wenn du in C++ einen Integer mit 0x12345678 initialisierst, dann hat der auch GENAU diesen Wert, mit dem du dann GENAU so arbeiten kannst.
Was intern auf der Hardware passiert, muss dich (fast nie) nicht interessieren!
Nachtrag: Ich sehe gerade, dass ich den Adressoperator beim reinterpret_cast übersehen habe.
Korrekt muss das so aussehen:u
uint32_t x = 0x12345678;
auto y = *reinterpret_cast<uint8_t *>(&x);
Soweit war ich noch gar nicht, lese ich mir noch durch, danke =)
Super danke, soweit war ich doch schon, außer die Geschichte mit den casts. Hat mich übel verwirrt, weil ich davor mal bootloader geschrieben habe, bzw. bisschen damit gespielt und Hexzahlen ausgegeben und bla bla bla und dort gabs halt Big und Little Endian :D
=)
Ich müsste das auch in der c99-Spezifikation nachlesen, aber an sich müsste sich das, wenn überhaupt, die niederwertigsten Bits schnappen. Vielleicht ist das aber auch undefiniert.
EDIT:
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf
Seite 55 (bzw. 43);
When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.
Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.
Sprich der Wert bleibt unverändert, wenn er im range von uint8_t liegt. Wenn nicht, dann werden die höherwertigen Bits quasi abgeschnitten.
Hm, stimmt, das ist ja C++. Ich denke mal, da wird das nicht anders sein.
Siehe Ergänzung, der Frage es war mal wieder peinlich was ich hier getan habe.
Muss dir nicht peinlich sein, an sich eine durchaus berechtigte Frage. Gibt anderes Zeug, das nicht definiert ist, wo man es aber erwarten würde.
wenn das ein datenstream wäre dann würde es wohl nur die ersten bits geben . aber du streamst ja nicht via bit .
du schneidest 8 bits raus und zwar die ersten 8 bits , also die von rechts . der wert wird ja irgendwo in einem register stehen und dann wird sich halt nur das zu verarbeitente geschnappt . oder hat man nur 8 bit register ?
also 32 bit wert (bei einem 64er system wohl 64 bit) , werden maximal 8 bit interpretiert und das natürlich beginnend bei bit hoch 0
passt scho, habs rausgefunden, war mal wieder viel zu blöd. Siehe Ergänzung.
Also im Endeffekt willst Du doch die 8 bit haben, die "ganz rechts" stehen. Meiner Meinung nach wäre es also doch so, dass wenn Du die Gefahr umgehen willst, dass die vorderen 24 Bit irgendwie komisch interpretiert werden, erstmal 24 bit nach links shiften müsstest, dann wieder 24 bit nach rechts. Damit sind alle bits 0 bis auf die, die Du haben möchtest.
Dann machst Du einfach einen normalen cast - damit schneidest Du doch die überschüssigen 24 Bit einfach ab. Oder verstehe ich was falsch?
Du könntest vermutlich auch einfach ein & 0xFF machen und das casten.
Jetzt hab ich dich verwirrt und ich hab alle anderen verwirrt. Ich muss überhaupt nicht shiften, der krallt sich die letzten 8 bit von meinen 32 bit uint und damit hab ich genau das, was ich haben will.
War nur mal wieder ultra verwirrt weil bei meiner Ausgabe nichts kam, naja wie auch, der interpretiert uint_t in Teilen als char und dann kommt keine Zahl, sondern ein Zeichen, super. Das wars mal wieder komplett. xD
Trotzdem danke :D
nutze den visual studio compiler, g++ bringt auch nichts besseres.
Bei c99 hänge ich nicht mehr xD