Verbreitung von Endianess



  • Ich lese immer wieder, dass es litte- und bigendian Zahlenformate gibt. Jetzt habe ich gelesen, dass die "normalen" Zahlen, fast immer little endian benutzen.

    Wenn ich also als C++ Programmierer für normale Heim PC's mit normalen Betriebssystemen (windows, linux) und normalen Compilern (Visual Studio, CodeBlocks) programmiere, muss ich mir gedanken machen, dass meine unsigned ints ein anderes Format haben könnten, als Little Endian?



  • Intels haben andere Endianness als die PowerPC-Apples. Zur Konvertierung stellt das OS die htons bzw. htonl - Funktionen zur Verfuegung. (google ist dein freund)



  • htons (host to net short) und htonl (host to net long) sind erstmal nur dafür da, IP-Header Standardkonform zu befüllen und helfen einem nur bei 16 Bit bzw. 32 Bit unsigned Integer Werten. Für Endian-Konvertierung sind diese nur sehr bedingt geeignet und auch nicht gedacht.
    Weitgehende plattfromunabhängigkeit ist es am Besten, seine Werte als ASCII-String zu übertragen/speichern.
    Zu Deiner Frage: Viele üblicherweise verwendete Plattformen sind heute Little Endian. Das gilt vor allem für alles x86 basierte.
    Wenn Du portable Internetapplikationen schreibst, dann musst Du dabei aber noch Big Endian und ggf. sogar Mixed Endian beachten.



  • Außerdem muß gutes Programmieren weh tun. Mehr Regeln, die irgendwas regeln. Single-Entry-Singe-Exit, vorher malen und Struktogramme malen, Variablen mit UN verzieren und viel mehr Templates und stl und boost verwenden.
    Nur bittere Medizin ist gut. Siehe auch Memtheorie und die Entstehung von Religionen.



  • dass meine unsigned ints ein anderes Format haben könnten, als Little Endian?

    Sofern du nichts ueber das Netzwerk schickst oder Bitoperationen ausfuehrst, sollte es keine Rolle spielen.


  • Mod

    knivil schrieb:

    ...Bitoperationen...

    Die sollten nicht von endianess betroffen sein. Neben Netzwerkoperationen fallen mir spontan nur Sachen wie reinterpret_cast ein, bei denen Muster im Speicher interpretiert werden.



  • oder unions

    union dword
    {
        unsigned long dw_value;
        unsigned char b_values[4];
    };
    

    theoretisch verbietet der standard sowas ja eh:

    dword dw;
    dw.dw_value = 0xDEADBEEF;
    
    for (int n = 0; n < 4; ++n)
        // do something with dw.b_values[n]
    

    aber in der Praxis funktionierts ja^^

    ähnliche Relevanz gibt es hier:

    unsigned long value = 0xDEADBEEF;
    unsigned char* bytes = (unsigned char*)(&value);
    // bzw. reinterpret_cast<unsigned char*>(&value)
    

    hier kommt es auch auf die Endians an, welche Werte letztlich in bytes[1 - 4] stehen



  • einfacher Test des Endians:

    namespace Endian
    {
        const int Little = 0;
        const int Big = 1;
        const int Middle = 2;
    };
    
    // gibt die "Endianität" des lokalen Systems zurück
    int check_endian(void)
    {
        unsigned long test_value = 0xDEADBEEF;
        unsigned char test_byte = *(reinterpret_cast<unsigned char*>(&test_value));
        switch (test_byte)
        {
        case 0xDE: return Endian::Big; break;
        case 0xEF: return Endian::Little; break;
        }
        return Endian::Middle;
    }
    

    Erklärung zu den Endians:
    http://de.wikipedia.org/wiki/Byte-Reihenfolge



  • Gondur schrieb:

    Ich lese immer wieder, dass es litte- und bigendian Zahlenformate gibt.

    Hmm... Zahlenformat? Würde eher "Speicherlayout" sagen.

    Gondur schrieb:

    Wenn ich also als C++ Programmierer für normale Heim PC's mit normalen Betriebssystemen (windows, linux) und normalen Compilern (Visual Studio, CodeBlocks) programmiere, muss ich mir gedanken machen, dass meine unsigned ints ein anderes Format haben könnten, als Little Endian?

    Also...

    Wenn bei Dir "normal" = X86 bedeutet, dann wird auf "normalen" Rechnern Little-Endian benutzt.

    Das hat Dich aber eigentlich weniger zu interessieren, wenn Du portabel programmieren willst. Und portabel programmieren ist gar nicht so schwer. Lass einfach die Finger von den Dingen, die der C++ Standard als "implementation-defined" klassifiziert. Wie eine int-Variable im Speicher liegt, ist nunmal "implementation-defined". Das spielt erst eine Rolle, wenn man auf Teile solcher Variablen auf Byte-Ebene zugreift (siehe DrakoXP's Antwort, müsste nur switch (test_byte) statt switch (test_value) heißen).



  • senfer schrieb:

    müsste nur switch (test_byte) statt switch (test_value) heißen

    ah sry, verschrieben xD
    aber hast recht 😉
    EDIT: habs angepasst 😃

    naja, problematisch könnte es vllt auch werden, wenn man eine Datei voller binary ints hat,
    diese per Netzwerk verschickt und der andere Rechner die ints dann wie sie sind ausliest.
    arbeiten beide Rechner mit unterschiedlichen endians, so kommts auch zu Konflikten



  • DrakoXP schrieb:

    einfacher Test des Endians:

    namespace Endian
    {
        const int Little = 0;
        const int Big = 1;
        const int Middle = 2;
    };
    
    // gibt die "Endianität" des lokalen Systems zurück
    int check_endian(void)
    {
        unsigned long test_value = 0xDEADBEEF;
        unsigned char test_byte = *(reinterpret_cast<unsigned char*>(&test_value));
        switch (test_byte)
        {
        case 0xDE: return Endian::Big; break;
        case 0xEF: return Endian::Little; break;
        }
        return Endian::Middle;
    }
    

    Einfach? LOL, so gehts einfach:

    bool isLittleEndian()
    {
      int a = 0x0102;
      return *(char*)&a == 0x02;
    }
    

    Was soll überhaupt "Endian::Middle" sein?



  • siehe Wikipedia, meinst du ich poste den Link umsonst?

    natürlich ist deines einfacher, aber bei dir wird halt NUR auf Little Endian getestet
    (ok, das reicht im Normalfall aus, aber meines ist eben präziser xD)
    kann ja vorkommen, dass irgendjemand so ein Uraltsystem mit Middle-Endian hat,
    wobei Middle-Endian noch nicht mal einheitlich ist, wenn ich Wikipedia richtig verstanden habe...

    Wikipedia schrieb:

    Im folgenden Beispiel wird die Ganzzahl 439.041.101 als 32-Bit-Integer-Wert gespeichert (Binär: 00011010 00101011 00111100 01001101, hexadezimal: 1A 2B 3C 4D). Die Speicherung erfolgt ab einer hypothetischen Speicheradresse 10000 und den darauf folgenden drei Bytes.

    Wenn die Speicherung in der Reihenfolge 1A 2B 3C 4D erfolgt, entspricht dies Big Endian. Die Speicherung in der umgekehrten Reihenfolge (4D 3C 2B 1A), also das am wenigsten signifikante Byte an der niedrigsten Speicheradresse, entspricht dagegen Little Endian. Einige ältere Systeme (z. B. PDP-11) speichern die Daten auch in der Reihenfolge 3C 4D 1A 2B oder auch 2B 1A 4D 3C. Dies wird als Middle Endian bezeichnet.



  • Ach, Wikipedia. Theoretisch gäbe es bei einem 4-Byte Wert 24 mögliche Anordnungen. Praktisch muss man nur 0123 oder 3210 berücksichtigen. Ohnehin ist es ratsam, schon vor dem compilieren die Endianess zu kennen. Man muss nicht davon ausgehen, dass sie sich bei laufendem Programm ändert. Daher sind unsere beiden Funktionen sowieso fürn Arsch.



  • C++Fan 2009 schrieb:

    Ohnehin ist es ratsam, schon vor dem compilieren die Endianess zu kennen. Man muss nicht davon ausgehen, dass sie sich bei laufendem Programm ändert.

    nicht direkt zur Laufzeit,
    aber du könntest jemand anderes ein kompiliertes Programm geben und der hätte ein anderes Endian als du, dann wäre es schlecht, wenn das Endian Hard Coded im Programm wäre 😉

    wobei das natürlich alles Recht unwahrscheinlich ist, da ja eben fast alle "normalen" Heim-PCs mit Little Endian arbeiten.



  • SeppJ schrieb:

    knivil schrieb:

    ...Bitoperationen...

    Die sollten nicht von endianess betroffen sein. Neben Netzwerkoperationen fallen mir spontan nur Sachen wie reinterpret_cast ein, bei denen Muster im Speicher interpretiert werden.

    Das Speichern von Binaerdaten in einer externen Datei ist auch ein Thema, falls du ein Programm hast, das auf verschiedenen Systemen laeuft.



  • *doppelpost*



  • Blue-Tiger schrieb:

    Das Speichern von Binaerdaten in einer externen Datei ist auch ein Thema, falls du ein Programm hast, das auf verschiedenen Systemen laeuft.

    Nicht wirklich. Du musst dich halt entscheiden, mit welchem Endian du deine Werte serialisiert.



  • Nanyuki schrieb:

    Blue-Tiger schrieb:

    Das Speichern von Binaerdaten in einer externen Datei ist auch ein Thema, falls du ein Programm hast, das auf verschiedenen Systemen laeuft.

    Nicht wirklich. Du musst dich halt entscheiden, mit welchem Endian du deine Werte serialisiert.

    Genau das wollt ich damit sagen 😉



  • DrakoXP schrieb:

    ...
    
    int check_endian(void)
    {
        unsigned long test_value = 0xDEADBEEF;
        unsigned char test_byte = *(reinterpret_cast<unsigned char*>(&test_value));
        switch (test_byte)
        {
        case 0xDE: return Endian::Big; break;
        case 0xEF: return Endian::Little; break;
        }
        return Endian::Middle;
    }
    

    Schon auf Linux 64-Bit probiert? Kann ich gerade nicht testen, da müsste aber test_byte==0 gelten, da dort ein long aus 64 Bit besteht (LP64-Modell).

    Wenn's um binäre Ein/Ausgabe und portable Dateiformate geht, muss man eben seine Zahlen per Hand in Bytes zerlegen bzw mehrere Varianten von Lese/Schreib-Funktionen anbieten, die je nach Architektur verwendet werden. Ich setze dabei aber meist CHAR_BIT == 8 voraus:

    #include <climits>
    #if CHAR_BIT != 8
    #error "Watt willst Du denn? Keine 8-bit Bytes?"
    #endif
    

    Preisfrage ohne Preis: Wie müsste man eine Funktion bauen, welche eine vorzeichenbehaftete 32-Bit Zahl im Little-Endian & Zweierkomplement Format aus einem char-Puffer holt?

    signed long read_32bit_le_signed(unsigned char* woher)
    {
      ...
    }
    

    Versucht sie so portabel wie möglich hinzubekommen, ohne ein bestimmtes "implementation-defined behavior" vorauszusetzen. Als einzige Ausnahme sei CHAR_BIT==8 gegeben 🙂



  • wie groß sind die Datentypen bei 64 Bit überhaupt?

    bei 32 Bit is ja:

    char 8 bit
    short 16 bit
    int 32 bit (kann auch 16 bit sein, so weit ich weiß, also 32/16)
    long 32 bit
    long long 64 bit

    wie soll das dann bei 64 Bit aussehen,
    wenn da ein long 64 bit ist?
    long long ist dann 128 bit, ok
    aber reicht dann int von 16 über 32 bis 64 bit je nach implementierung?
    oder ist dann ein int konstant 32 bit und liegt somit zwischen short und long?
    oder ist int wieder gleich dem long,
    und short wird auf 32 bit hochgesetzt?

    das gibt einem wahrlich zu denken...


Anmelden zum Antworten