endianness bestimmen
-
TyRoXx schrieb:
Man kann die Endianness zwar so bestimmen, aber meiner Erfahrung nach nützt diese Information außerhalb von Kernel-Treibern oder so nichts. Und da würde man das dann vernünftig über plattformspezifische Makros unterscheiden.
Wofür glaubst du die Information denn zu benötigen?Z.B. für das lesen von Daten, die mit
fwrite
geschrieben wurden.
-
DirkB schrieb:
TyRoXx schrieb:
Man kann die Endianness zwar so bestimmen, aber meiner Erfahrung nach nützt diese Information außerhalb von Kernel-Treibern oder so nichts. Und da würde man das dann vernünftig über plattformspezifische Makros unterscheiden.
Wofür glaubst du die Information denn zu benötigen?Z.B. für das lesen von Daten, die mit
fwrite
geschrieben wurden.Da kann man shiften und hat die Daten immer im selben Format.
-
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ärechar c[sizeof(int)]
undhelper{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.
-
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.
-
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.
-
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ärechar c[sizeof(int)]
undhelper{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...