endianness bestimmen
-
+1 zu Nathan, erst zeigst du deinen Code und dann erklärst du, wieso man ihn nicht braucht.
Zum Code: Ich würde
uint32_t
aus <cstdint> nehmen, dann ist das schonmal etwas sicherer. Undif(endian_test.cdata[0] == 0x78) { return true; } return false;
würde ich lieber ohne if schreiben...
Der Code ist natürlich UB, aber er sollte auf den üblichen Plattformen funktionierten.
-
ok.
ohne if, wie meinst das? warum uint32_t?
kann man endianness zur compile time bestimmen?
-
Dafür gibt es
ntohl
und so:bool is_big_endian = (ntohl(1) == 1);
Die Funktion ist zwar nicht im Standard, aber jeder Compiler liefert die in irgendwelchen Headern mit.
Keine Sorge, Compiler optimieren den Aufruf weg.Es gibt noch eine andere Möglichkeit.
uint32_t test_value = 0x12345678; bool is_big_endian = (reinterpret_cast<char *>(&test_value)[0] == 0x12);
Die "clevere" Lösung mit dem
union
würde ich nicht empfehlen, weil undefiniertes Verhalten für Menschen verwirrend ist. Eine portable Lösung erfordert nur C++-Kenntnisse, eine unportable mitunion
erfordert zusätzliche Plattformkenntnisse zum Verständnis.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?
-
coder007 schrieb:
kann man endianness zur compile time bestimmen?
http://stackoverflow.com/a/4240014
Da ist auch eine Runtime-Methode, die zumindest nicht die strict aliasing rule verletzt. Lesen ist aber mindestens implementation-defined, wenn nicht ebenfalls UB.
-
@TyRoXx: das ntohl ist zwar nett zu erwaehnen, aber reicht wohl nicht aus, wenn der interviewer code sehen will...
-
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.