Forum: PC-Programmierung Erklärung C-Code


von Dennis S. (eltio)


Lesenswert?

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
1
static int GetSystemEndianness(void)
2
{
3
    union {
4
        short s;
5
        char c[sizeof(short)];
6
    } un;
7
8
    un.s = 0x0102;
9
10
    if(sizeof(short) == 2)
11
    {
12
        if(un.c[0] == 1 && un.c[1] == 2)
13
            return ENDIAN_BIG;
14
        else if(un.c[0] == 2 && un.c[1] == 1)
15
            return ENDIAN_LITTLE;
16
        else
17
            return ENDIAN_UNKNOWN;
18
    }
19
    else
20
    {
21
        return ENDIAN_UNKNOWN;
22
    }
23
}

von Markus B. (russenbaer)


Lesenswert?

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

von TestX .. (xaos)


Lesenswert?

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! :)

von Wayne (Gast)


Lesenswert?

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.

von Dennis S. (eltio)


Lesenswert?

Andi ... schrieb:
> Union sorgt dafür, dass die Variablen im gleichen! speicherbereich

Stimmt, da war ja was.. Danke für die Auffrischung.

Gruß
Dennis

von Robert L. (lrlr)


Lesenswert?

wobei das wohl eher "Lehrbuch" Code ist?
jemand der täglich C Programmiert, würde das wohl in einer (kurzen) 
Zeile Unterbringen..

von Dennis S. (eltio)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Der Ausdruck
1
*(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:
1
*(int8_t*)&(int16_t){1}

Dieser zweite Ausdruck hat zudem den Vorteil, dass er vom Compiler
(zumindest vom GCC) zur Compile-Zeit berechnet wird.

: Bearbeitet durch Moderator
von unwissender (Gast)


Lesenswert?

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?

von Felix P. (fixxl)


Lesenswert?

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.

von G. H. (schufti)


Lesenswert?

Yalu X. schrieb:

>
1
> *(int8_t*)&(int16_t){1}
2
>
>
> 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

: Bearbeitet durch User
von Guest (Gast)


Lesenswert?

Felix Pflaum schrieb:
> oder bei 1, 0 den Wert 256,  da das Bit 8 als
> einziges gesetzt ist

Du meinst wohl eher 128...

von npn (Gast)


Lesenswert?

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

von Daniel A. (daniel-a)


Lesenswert?

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!

von Dennis S. (eltio)


Lesenswert?

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

von Robert L. (lrlr)


Lesenswert?

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.

von Fabian O. (xfr)


Lesenswert?

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:
1
static int GetSystemEndianness(void)
2
{
3
    unsigned short s = 0x0102;
4
    unsigned char c[2];
5
    memcpy(c, &s, 2);
6
7
    if(sizeof(s) == 2)
8
    {
9
        if(c[0] == 1 && c[1] == 2)
10
            return ENDIAN_BIG;
11
        else if(c[0] == 2 && c[1] == 1)
12
            return ENDIAN_LITTLE;
13
        else
14
            return ENDIAN_UNKNOWN;
15
    }
16
    else
17
    {
18
        return ENDIAN_UNKNOW
19
    }
20
}

von Robert L. (lrlr)


Lesenswert?

@ Fabian

aber das ist doch der (einzige!) sinn vom Unions?
wenn das nicht garantiert wäre, wäre das irgendwie "komisch"

von Oliver S. (oliverso)


Lesenswert?

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

von nicht"Gast" (Gast)


Lesenswert?

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 :)

von Gustav (Gast)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Fabian O. (xfr)


Lesenswert?

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 ...

von Oliver S. (oliverso)


Lesenswert?

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

von Robert L. (lrlr)


Lesenswert?

es würde noch "__packed union" geben  (oder ist das auch nicht im 
"standard") ?

das müsste "klarer" definiert sein?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.