endianness bestimmen



  • DirkB schrieb:

    Z.B. für das lesen von Daten, die mit fwrite geschrieben wurden.

    So kann man das machen:

    uint32_t value = 0;
    fread(&value, sizeof(value), 1, file);
    value = ntohl(value);
    

    Wo genau musste ich nun die Endianness ermitteln?

    Falls auf einer absurden Plattform ntohl nicht verfügbar ist, kann man das hier nehmen:

    uint32_t ntohl(uint32_t big)
    {
    	unsigned char *bytes = (unsigned char *)&big;
    	return
    		(uint32_t(bytes[0]) << 24) |
    		(uint32_t(bytes[1]) << 16) |
    		(uint32_t(bytes[2]) <<  8) |
    		(uint32_t(bytes[3]));
    }
    

    Das geht immer, wenn unsigned char 8 Bits hat. Brauchte ich hier irgendwo die Endianness?
    Und was, wenn der Prozessor weder Big- noch Little-Endian hat, sondern eine andere Byte-Reihenfolge (ja, so etwas gibt es). Meine portable Lösung funktioniert auch da.



  • Und wenn die Daten nicht in der Network Byte Order vorhanden sind? ²

    ntohl vertauscht die Bytes auf Little Endian Systemen.
    Auf Big Endian Systemen wird nichts vertauscht.

    ² Klar, ist ein Designfehler. Aber so werden nunmal viele Daten von x86 Systemen gespeichert.



  • DirkB schrieb:

    Und wenn die Daten nicht in der Network Byte Order vorhanden sind?

    Dann dreht man die eben um.

    uint32_t swap_bytes_32(uint32_t value)
    {
    #ifdef __GNUC__
    	//plattformspezifische Optimierung
    	return __builtin_bswap32(value);
    #else
    	//portabler Fallback
    	char *data = reinterpret_cast<char *>(&value);
    	std::reverse(data, data + sizeof(value));
    	return value;
    #endif
    }
    
    uint32_t value = 0;
    fread(&value, sizeof(value), 1, file);
    value = ntohl(swap_bytes_32(value));
    

    Auch hier ist es nicht nötig die Endianness zu kennen.
    GCC erkennt das doppelte Vertauschen auf Little-Endian mit Intrinsics wie __builtin_bswap32 übrigens und optimiert es weg.

    EDIT: Ich hatte zuerst swap_bytes_32(ntohl(value)) geschrieben, aber das funktioniert nur auf Little- und Big-Endian-Plattformen.



  • union deadbeef - google einfach nach deadbeef, da sollte schon was dabei rauskommen.



  • coder007 schrieb:

    kann man endianness zur compile time bestimmen?

    Sollte funktionieren:

    constexpr bool is_little_endian()
    {
        union helper {
            int i;
            char c;
        };
        return helper{1}.c;
    }
    

    Edit: garantiert der Standard, dass helper::c im 1. Byte von helper liegt? Ansonsten wäre char c[sizeof(int)] und helper{1}.c[0] wohl ehr Standardkonform.



  • osdt schrieb:

    Edit: garantiert der Standard, dass helper::c im 1. Byte von helper liegt?

    Das ist vollkommen egal, da das Lesen des nicht aktiven Unionmembers sowieso UB ist.



  • osdt schrieb:

    Edit: garantiert der Standard, dass helper::c im 1. Byte von helper liegt?

    Damit ist das geklärt:

    N3337 $9.5 schrieb:

    ... Each non-static data member is allocated as if it were the sole member of a struct

    Nathan schrieb:

    Das ist vollkommen egal, da das Lesen des nicht aktiven Unionmembers sowieso UB ist.

    Das bezieht sich meines Wissens nur auf nicht-triviale Typen, deren Konstuktor explizit aufgerufen werden muss. Nur in diesem Zusmmenhang macht der Term 'aktiver Member' überhaupt Sinn. Ich lasse mich natürlich gern eines Besseren belehren.


  • Mod

    Das ist vollkommen egal, da das Lesen des nicht aktiven Unionmembers sowieso UB ist.

    Das ist (pauschal) offensichtlich falsch.

    Lesen ist aber mindestens implementation-defined, wenn nicht ebenfalls UB.

    Welches Lesen?



  • osdt schrieb:

    Nathan schrieb:

    Das ist vollkommen egal, da das Lesen des nicht aktiven Unionmembers sowieso UB ist.

    Das bezieht sich meines Wissens nur auf nicht-triviale Typen, deren Konstuktor explizit aufgerufen werden muss. Nur in diesem Zusmmenhang macht der Term 'aktiver Member' überhaupt Sinn. Ich lasse mich natürlich gern eines Besseren belehren.

    Arcoth schrieb:

    Das ist vollkommen egal, da das Lesen des nicht aktiven Unionmembers sowieso UB ist.

    Das ist (pauschal) offensichtlich falsch.

    type-punning via union verletzt die strict aliasing rule (§3.10/10), das ist UB.

    Lesen ist aber mindestens implementation-defined, wenn nicht ebenfalls UB.

    Welches Lesen?

    In dem verlinktem Code:

    bool isLittleEndian()
    {
        short int number = 0x1;
        char *numPtr = (char*)&number;
        return (numPtr[0] == 1); // hier
    }
    

    Aliasing via char* ist erlaubt, aber wenn man das dann als char liest, ist das Ergebnis definitiv nicht portabel.


  • Mod

    Aliasing via char* ist erlaubt, aber wenn man das dann als char liest, ist das Ergebnis definitiv nicht portabel.

    Ja, die value representation ist implementation-defined. UB ist es aber definitiv nicht, sondern explizit erlaubt.

    type-punning via union verletzt die strict aliasing rule (§3.10/10), das ist UB.

    Kleiner Tipp: Die Member einer Union müssen nicht alle von völlig unterschiedlichem Typ sein. 😉



  • Arcoth schrieb:

    Aliasing via char* ist erlaubt, aber wenn man das dann als char liest, ist das Ergebnis definitiv nicht portabel.

    Ja, die value representation ist implementation-defined. UB ist es aber definitiv nicht, sondern explizit erlaubt.

    Ja, deshalb schrieb ich das ja.

    type-punning via union verletzt die strict aliasing rule (§3.10/10), das ist UB.

    Kleiner Tipp: Die Member einer Union müssen nicht alle von völlig unterschiedlichem Typ sein. 😉

    *sigh*
    Ja, dann verletzt es nicht die strict aliasing rule. Garantiert das man den selben Wert bekommt, den man rein gesteckt hat ist das aber glaub ich immer noch nicht. 😉


  • Mod

    osdt schrieb:

    coder007 schrieb:

    kann man endianness zur compile time bestimmen?

    Sollte funktionieren:

    constexpr bool is_little_endian()
    {
        union helper {
            int i;
            char c;
        };
        return helper{1}.c;
    }
    

    Edit: garantiert der Standard, dass helper::c im 1. Byte von helper liegt? Ansonsten wäre char c[sizeof(int)] und helper{1}.c[0] wohl ehr Standardkonform.

    Sollte nicht funktionieren.

    2 A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the
    abstract machine (1.9), would evaluate one of the following expressions:
    [...]
    — an lvalue-to-rvalue conversion (4.1) or modification (5.17, 5.2.6, 5.3.2) that is applied to a glvalue that
    refers to a non-active member of a union or a subobject thereof;

    Das Standardkomitee hat solche Konstrukte ganz bewusst nicht konstant gemacht um die Implementation u.a. von cross-Compilern nicht zu erschweren.



  • LOL, Extrapunkte für osdt, weil er ein Konstrukt geschrieben hat, dass explicit vom Standard verboten ist. Respekt.



  • was ist nun die eine moegliche loesung? 🙂



  • Nathan schrieb:

    LOL, Extrapunkte für osdt, weil er ein Konstrukt geschrieben hat, dass explicit vom Standard verboten ist. Respekt.

    Da kannst du mal sehen was mit etwas gutem Willen alles möglich ist 😃 GCC (4.9.1) compiliert das ohne Murren und ich hab gedacht 'wird schon passen'.

    Leider musste ich eben feststellen, dass clang (3.5) anderer Meinung ist:

    prog.cc:10:22: note: read of member 'c' of union with active member 'i' is not allowed in a constant expression
    

    Tja, dumm gelaufen. Ich nehme alles zurück und gelobe Besserung.



  • coder007 schrieb:

    vorbereitung coding interview...

    Wie kommst du eigentlich gerade auf diese Frage?

    coder007 schrieb:

    was ist nun die eine moegliche loesung? 🙂

    Mögliche Lösung: In C++ gibt es keine Endianness.



  • Whenever I see code that asks what the native byte order is, it's almost certain the code is either wrong or misguided.



  • @pike: as i said its a coding interview question...


Anmelden zum Antworten