Fragensteller
Alignment Problem oder Compiler-Optimierung?

Frage
-
Hallo,
Ich nutze ein Visual Studio 2005 Standard unter einem Windows XP.
Ich lege hier bei einem VC++-Projekt Aufgrund einer speziellen Anforderung an die Datenhaltung konstante Daten in folgendem Format an:
static unsigned int const stringVal[] = { 'Stri', 'ngVA','L\0\0\0' };
Ich greife also über den array index wieder auf diese Daten zu und je nach Art der Verwendung werden die Daten dann auch konvertiert.
Hierbei stelle ich allerdings fest, dass, wenn ich über den Index auf die 32bit-Werte zugreife und sie in ein unsigned char Array umwandeln will, z.B. sounsigned char value[4]; value[0] = (unsigned char)((stringVal[i] >> 24) & 0xFF); value[1] = (unsigned char)((stringVal[i] >> 16) & 0xFF); value[2] = (unsigned char)((stringVal[i] >> 8) & 0xFF); value[3] = (unsigned char)(stringVal[i] & 0xFF);
das ganze nicht wieder so bei mir aus dem Speicher ankommt, wie ich das erwarte.
Sobald in dieser letzten 32-Bit-Konstante zwei "\0" nacheinander kommen, wird sie offenbar nicht mehr als unsigned int im Speicher abgelegt, wie die anderen, sondern es sieht so aus, als wäre sie als unsigned short abgelegt worden.
Also im Code steht: 'L\0\0\0' - aus dem Speicher, wenn als unsigned int (32bit) gelesen, kommt aber ein '\0\0L\0'.
Im Moment habe ich zwei mögliche Workarounds, die mir aber beide nicht so richtig gefallen.
Erstens: Ich mache aus den überzähligen \0 einfach dummy-Nutzdaten, also z.B.: 'L\0aa', was dann wieder so aus dem Speicher kommt, wie ich das erwartet hätte.
Zweitens: Ich lege die Daten nicht als lesbare Daten an, sondern als Hex-Zahl wie z.B. 0x4C000000, was natürlich auch den gewünschten Effekt hätte ...
Kann mir das jemand näher erklären, bzw. einen Ansatz zur Lösung des Problems nennen?
Vielen Dank für Eure Denkanstöße oder Lösungsansätze.
Grüße
Markus
- Bearbeitet mplessing Dienstag, 21. Februar 2012 11:38
Alle Antworten
-
Dein Problem ist denke ich etwas komplizierter. Ich bringe einmal ein paar Beispiele.
Wenn Du '\x50\x51\x52\x53' (entspricht auch "PQRS") , hast Du einen Hex Wert von 0x53525150
Wenn Du '\x50\x51\x52S' (entspricht auch "PQRS") , hast Du einen Hex Wert von 0x53525150
Wenn Du '\x50\x51RS' (entspricht auch "PQRS") , hast Du einen Hex Wert von 0x53525150
Wenn Du '\x50QRS' (entspricht auch "PQRS") , hast Du einen Hex Wert von 0x50515253Wenn Du 'PQRS' verwendest, hast Du einen Hex Wert von 0x50515253
Wenn Du 'PQR\x53' (entspricht auch "PQRS") verwendest, hast Du einen Hex Wert von 0x50515253
Wenn Du 'PQ\x52\x53' (entspricht auch "PQRS") verwendest, hast Du einen Hex Wert von 0x53505152
Wenn Du 'P\x51\x52\x53' (entspricht auch "PQRS") , hast Du einen Hex Wert von 0x53525051Wenn Du '\x50Q\x52\x53' (entspricht auch "PQRS") , hast Du einen Hex Wert von 0x53525051
Wenn Du '\x50QR\x53' (entspricht auch "PQRS") , hast Du einen Hex Wert von 0x53505152So wie es aussieht interpretiert der Compiler Strings mit "\xnn" bzw "\nnn" Angaben teilweise irgendwo anders, wie ohne. Genau begründen kann ich es jetzt nicht, wieso Du auf den Bauch fällst. Ich bin mir auch ein Stück weit nicht sicher ob der Ausdruck den Du dort verwendest in Verbindung mit int Variablen wirklich eindeutig ist. Normalerweise hat ein String eine Nullterminierung, die in Deiner Verwendung schon mal flach fällt. Es ist jedoch Augenfällig, dass die Zeichen mit \xxx mit hohe Wertigkeit rechts haben, und normale ASCII Zeichen die hohe Wertigkeit links.
Im Kontext mit Deiner Verwendung liefert übrigens
unsigned int const stringVal[] = { 'Stri', 'ngVA','\0\0\0L' };
die korrekten Ergebnisse aus meiner Warte. Die wären nämlich in den drei ints: 0x53747269, 0x6E675641, 0x4C000000
Wenn Du die nicht verwendeten Zeichen links mit \0 und nicht rechts auffülllst, dann sollte es denke ich klappen.
Alles in allem ist das von Dir verwendete Konstrukt aber schon sehr merkwürdig. Ich selber hätte jetzt auch nicht so ad hoc die Speicherausrichtung Deiner Konstanten mal so aufskizziert mit den Werten.
- Bearbeitet Bordon Dienstag, 21. Februar 2012 13:55
-
Hi Bordon,
danke für die ausführliche Analyse :-)
Ich hab auch nochmal etwas recherchiert und mich mit Kollegen unterhalten.
Die Verwendung einer "multicharacter charachter constant" ist prinzipiell im C++-Standard erlaubt, sind aber explizit als system-dependent, wenn nicht sogar als compiler-dependent markiert.
Siehe z.B. die Microsoft MSDN-Seite: http://msdn.microsoft.com/de-de/library/6aw8xdf2.aspx
Da eine character Konstante als integer vorgehalten wird muss die Angabe hier eigentlich eindeutig sein.
Mit meiner Interpretation, dass das ganze dann als unsigned short im Speicher abgelegt wird, war ich aber auf dem Holzweg.
Ich bin mir jetzt ziemlich sicher, dass der Compiler das ganze irgendwo als eine Art String sieht, der nicht zwingend eine Null-Terminierung braucht und erstmal die mehrfachen \0 wegoptimiert, weil es braucht ja nur eine oder keine String-Terminierung.
Er macht also zuerst aus 'L\0\0\0' ein 'L\0' und weil er ja eine Integer Konstante hat, die 4 Byte breit ist füllt er das ganze von links mit 0 auf -> also haben wir ein 0x00004C00
Analog funktioniert das auch mit: 'LL\0\0' -> Optimierung -> 'LL\0' -> daraus wird 0x004C4C00
Bei unserer komischen 4-Byte String Integer Datenhaltung muss ich also eigentlich nur immer komplette 4-Byte Blöcke interpretieren und ignoriere führende Nullen - bzw. sehe ich eine 0, weiß ich, dass ich am letzten Arrayplatz angekommen bin, aber noch nicht zwingend am letzten Zeichen.
Vielen Dank für den Denkanstoß!
Grüße
Markus