Erklärung C-Code

OP #3761373
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
}
#3761385
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
#3761389
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! :)
Gast #3761391
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.
Moderator #3762047
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.
#3762276
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.
#3762282
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
#3762325
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.
#3762329
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
}
#3762381
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
#3762420
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 ...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren