Hallo zusammen,
kann mir jemand die Funktion dieses Codeteils erklären?
Ich verstehe nicht ganz die Verwendung der union. Warum speichert man
den Wert 0x0102 nicht einfach direkt in einer short-Variablen?
Gruß
Dennis
Hallo,
Der Code püft ob das LSB oder das MSB im Speicher zuerst abgelegt werden
(Endianess).
Die Union wird verwendet um auf die einzelnen Bytes des shorts einzeln
zugreifen zu können so wie sie im Speicher liegen.
Zusätzlich wird noch geprüft ob short ein 2-byte Variablentyp ist.
lg
Markus
Weil die Funktion ermitteln soll, ob die jeweilige Platform big oder
little endian benutzt (reihenfolge der bytes in einem typ > 1 byte).
Union sorgt dafür, dass die Variablen im gleichen! speicherbereich
liegen - mit dem char-typ erfolgt so ein elementate zugriff auf die
bytes aus der short-variable
btw. zieh dir mal nen paar C grundlagen rein! :)
Union: Zwei unterschiedliche Variabletypen belegen den gleichen
Speicherbereich. Das bedeutet du kannst in diesem Fall entweder mit
einer Short- oder zwei Char-Variablen auf den gleichen Speicherbereich
zugreifen.
Je nachdem was man da nun zurückliest läst sich die Speicherorganisation
mit der obigen Funktion bestimmen.
Robert L. schrieb:> wobei das wohl eher "Lehrbuch" Code ist?> jemand der täglich C Programmiert, würde das wohl in einer (kurzen)> Zeile Unterbringen..
Wie sähe das aus? Der Ausschnitt kommt aus einem
Opensource-Feldbus-Stack.
was
Yalu X. schrieb:> Der Ausdruck> *(int16_t*)(int8_t[]){0,1}==1>> liefert für big endian true (1), sonst false (0).>> Bei diesem, noch kürzeren Ausdruck ist es genau umgekehrt:> *(int8_t*)&(int16_t){1}
könnte mir jemand diese ausdrücke erklären?
unwissender schrieb:> was>> Yalu X. schrieb:>> Der Ausdruck>> *(int16_t*)(int8_t[]){0,1}==1>>>> liefert für big endian true (1), sonst false (0).>>>> Bei diesem, noch kürzeren Ausdruck ist es genau umgekehrt:>> *(int8_t*)&(int16_t){1}>>> könnte mir jemand diese ausdrücke erklären?
In beiden Fällen werden Zeiger auf einen Datentypen als Zeiger auf einen
anderen Datentypen gecastet und anschließend dereferenziert.
Im ersten Fall ist es ein Array von zwei 8-Bit-Werten, 0 und 1. Der
Zeiger auf die erste Speicherstelle ist zeigt auf einen Speicherbereich
von 8 Bit, durch den Cast mit (int16_t*) vergrößert man den Bereich aber
auf 16 Bit. Im Bereich dieses gecasteten Zeigers stehen nun also beide
Arraywerte, die 0 und die 1. Mit dem allerersten * ruft man dann den
Wert, auf den der gecastete Zeiger zeigt, ab. Der Wert wird als
16-Bit-Zahl interpretiert und ergibt bei Speicherreihenfolge 0, 1 den
Wert 1 (Big Endian) oder bei 1, 0 den Wert 256, da das Bit 8 als
einziges gesetzt ist. Mit dem Vergleich == 1 wird das ausgewertet.
Im zweiten Fall verkürzt man den betrachteten Speicherbereich, indem man
von 16 Bit auf 8 Bit castet und dereferenziert. Je nach Endianness zeigt
der Zeiger dann in den Bereich, in dem nur Nullen stehen (Big Endian)
oder nicht.
>> Dieser zweite Ausdruck hat zudem den Vorteil, dass er vom Compiler> (zumindest vom GCC) zur Compile-Zeit berechnet wird.
tja, blöd nur, dass da die Compilerplattform für das Ergebnis steht,
nicht die Zielplattform ... nicht alles was cool aussieht ist es auch.
Und durch solchen Sch... ist das Crosscompilieren von "Möchtegerns" Code
eine Qual
Guest schrieb:> Felix Pflaum schrieb:>> oder bei 1, 0 den Wert 256, da das Bit 8 als>> einziges gesetzt ist>> Du meinst wohl eher 128...
256 stimmt schon. 2^8=256
G. H. schrieb:> tja, blöd nur, dass da die Compilerplattform für das Ergebnis stehl
Blödsin!! Der compiler muss die Endiannes des zielsystems kennen, um
zahlen richtiv herum abspeichern zu können. Da dass immer der fall ist,
ist das ergebnis NUR VON DER ZIELPLATFORM ABHÄNGIG!
Mag ja sein, dass die kurze Lösung schön "fancy" ist.. aber wenn schon
nach ein paar Posts die Diskussion über die Funktionsfähigkeit anfängt,
ist mir die klare, lange Version eindeutig lieber.
Gruß
Dennis
eigentlich nicht,
den Einwand: "die Compilerplattform für das Ergebnis steht"
hätte er auch bei der langen Version bringen können, und wäre dort auch
falsch gewesen..
ich würde sogar behaupten, dass es keine Sinn macht Endiannes erst zur
laufzeit zu prüfen..
(sondern zur compiletime mit _ORDER_BIG_ENDIAN_ oder sowas
ähnlichem.. )
zitat:
There is no standard, but on many systems including <endian.h> will give
you some defines to look for.
Streng genommen ist allerdings das Ergebnis der ersten Variante
undefiniert, da der C-Standard nur garantiert, dass man über den
gleichen Member eine Zahl speichern und wieder auslesen kann. In dem
Code wird aber über verschiedene Member auf die Union zugegriffen.
Portabler wärs mit memcpy:
Yalu X. schrieb:> Der Ausdruck> *(int16_t*)(int8_t[]){0,1}==1>> liefert für big endian true (1), sonst false (0).>> Bei diesem, noch kürzeren Ausdruck ist es genau umgekehrt:> *(int8_t*)&(int16_t){1}>> Dieser zweite Ausdruck hat zudem den Vorteil, dass er vom Compiler> (zumindest vom GCC) zur Compile-Zeit berechnet wird.
Er könnte m.E. auch den ersten Ausdruck schon zur Compilezeit auswerten.
Das sind doch auch alles zur Compilezeit bekannte konstante Werte.
Oliver
Robert L. schrieb:> aber das ist doch der (einzige!) sinn vom Unions?> wenn das nicht garantiert wäre, wäre das irgendwie "komisch"
Moin,
der Sinn von Unions war mal Speicher zu sparen. Das ist ja aber
mittlerweile obsoltet geworden :)
Robert L. schrieb:> aber das ist doch der (einzige!) sinn vom Unions?
Nein.
> wenn das nicht garantiert wäre, wäre das irgendwie "komisch"
Relevant für Garantien der Sprache C ist der Standard, nicht dein
persönliches Empfinden.
Oliver S. schrieb:> Er könnte m.E. auch den ersten Ausdruck schon zur Compilezeit auswerten.
Das ist richtig. Aber zumindest der GCC 4.9.1 scheint ihn nicht im Kopf
auswerten zu können, sondern legt das temporäre Array zur Laufzeit an.
Fabian O. schrieb:> Streng genommen ist allerdings das Ergebnis der ersten Variante> undefiniert,
Streng genommen ist jede Variante undefiniert, da du auch beim Umcasten
eines Pointers in undefined Land landest. Selbst bei der memcpy Variante
bist du undefined, da hier ein impliziter Cast auf einen void*
stattfindet, womit du auch hier undefined bist.
Robert L. schrieb:> @ Fabian>> aber das ist doch der (einzige!) sinn vom Unions?> wenn das nicht garantiert wäre, wäre das irgendwie "komisch"
Der Sinn ist eher, dass man verschiedenartige Objekte im gleichen
Speicher bzw. der gleichen Datenstruktur (Array, Liste, ...) ablegen
kann.
Zum Beispiel könnte ein Programm Nachrichten empfangen, die
unterschiedliche Felder mit unterschiedlicher Länge haben. Diese
Nachrichtentypen definiert man einzeln als Struct und außerdem eine
Union, die alle Structs enthält. Wenn man einen Zwischenspeicher für die
Nachrichten braucht, kann man jetzt einfach ein Array vom Typ der Union
benutzen. Der Compiler sorgt dafür, dass ein Arrayelement so groß ist,
dass jeder Nachrichtentyp reinpasst.
Er garantiert aber nicht, dass etwas sinnvolles raus kommt, wenn man in
die Union eine Nachricht A reinschreibt und dann versucht, eine
Nachricht B rauszulesen. Ein strenger Compiler könnte das im Sinne der
schnellen Programmausführung wegoptimieren, da das Ergebnis ja
undefiniert ist ...
Fabian O. schrieb:> Ein strenger Compiler könnte das im Sinne der> schnellen Programmausführung wegoptimieren, da das Ergebnis ja> undefiniert ist ...
Das wäre allerdings blöd, weil dann gefühlte 100% aller Programme, die
solche Funktionen durchführen, nicht mehr funktionieren würden ;)
Oliver