Erklärung C-Code

OP #3761373
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
}
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! :)
Gast
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
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.
Gast
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.
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
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:
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
}
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
Gast
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 :)
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 ...

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 12 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.