Gibt es eine programmatische Methode, um festzustellen, ob Sie sich in einer Big-Endian- oder Little-Endian-Architektur befinden oder nicht? Ich muss in der Lage sein, Code zu schreiben, der auf einem Intel- oder PPC-System ausgeführt wird, und genau denselben Code verwenden (dh keine bedingte Kompilierung).
c++
algorithm
endianness
Jay T.
quelle
quelle
Antworten:
Ich mag die Methode, die auf Typ Punning basiert, nicht - sie wird oft vom Compiler gewarnt. Genau dafür sind Gewerkschaften da!
Das Prinzip entspricht dem von anderen vorgeschlagenen Typfall, dies ist jedoch klarer - und gemäß C99 ist die Richtigkeit garantiert. gcc bevorzugt dies im Vergleich zum direkten Zeiger.
Dies ist auch viel besser als das Beheben der Endianness beim Kompilieren - für Betriebssysteme, die Multi-Architektur unterstützen (z. B. Fat Binary auf Mac OS X), funktioniert dies für beide ppc / i386, während es ansonsten sehr einfach ist, Dinge durcheinander zu bringen .
quelle
CHAR_BIT != 8
?Sie können dies tun, indem Sie ein int setzen und Bits maskieren. Der wahrscheinlich einfachste Weg ist jedoch die Verwendung der integrierten Netzwerkbyte-Konvertierungsoperationen (da die Netzwerkbyte-Reihenfolge immer Big Endian ist).
Ein bisschen Fummeln könnte schneller sein, aber dieser Weg ist einfach, unkompliziert und ziemlich unmöglich zu vermasseln.
quelle
BSWAP
Vorgang abzielen .Bitte lesen Sie diesen Artikel :
quelle
Sie können verwenden,
std::endian
wenn Sie Zugriff auf C ++ 20-Compiler wie GCC 8+ oder Clang 7+ haben.Hinweis:
std::endian
begann in<type_traits>
, wurde aber auf dem<bit>
Kölner Treffen 2019 verschoben . GCC 8, Clang 7, 8 und 9 haben es in,<type_traits>
während GCC 9+ und Clang 10+ es haben<bit>
.quelle
Dies erfolgt normalerweise zur Kompilierungszeit (speziell aus Leistungsgründen), indem Sie die vom Compiler verfügbaren Header-Dateien verwenden oder eigene Dateien erstellen. Unter Linux haben Sie die Header-Datei "/usr/include/endian.h"
quelle
Ich war überrascht, dass niemand die Makros erwähnt hat, die der Vorprozessor standardmäßig definiert. Diese variieren je nach Plattform. Sie sind viel sauberer, als einen eigenen Endian-Check schreiben zu müssen.
Beispielsweise; Wenn wir uns die integrierten Makros ansehen, die GCC definiert (auf einem X86-64-Computer):
Auf einer PPC-Maschine bekomme ich:
(Die
:| gcc -dM -E -x c -
Magie druckt alle eingebauten Makros aus).quelle
echo "\n" | gcc -x c -E -dM - |& grep -i 'endian'
nichts zurück, während gcc 3.4.3 (von/usr/sfw/bin
sowieso) in Solaris eine Definition in dieser Richtung hat. Ich habe ähnliche Probleme bei VxWorks Tornado (gcc 2.95) -vs- VxWorks Workbench (gcc 3.4.4) gesehen.Ehm ... Es überrascht mich, dass niemand bemerkt hat, dass der Compiler den Test einfach optimiert und ein festes Ergebnis als Rückgabewert festlegt. Dies macht alle obigen Codebeispiele praktisch unbrauchbar. Das einzige, was zurückgegeben werden würde, ist die Endianness zur Kompilierungszeit! Und ja, ich habe alle oben genannten Beispiele getestet. Hier ist ein Beispiel mit MSVC 9.0 (Visual Studio 2008).
Reiner C-Code
Demontage
Vielleicht ist es möglich, JEDE Optimierung zur Kompilierungszeit nur für diese Funktion zu deaktivieren, aber ich weiß es nicht. Andernfalls ist es möglicherweise möglich, es in der Baugruppe fest zu codieren, obwohl dies nicht portabel ist. Und selbst dann könnte sogar das optimiert werden. Ich denke, ich brauche einen wirklich beschissenen Assembler, implementiere den gleichen Code für alle vorhandenen CPUs / Befehlssätze und na ja ... egal.
Außerdem hat hier jemand gesagt, dass sich die Endianness während der Laufzeit nicht ändert. FALSCH. Es gibt Bi-Endian-Maschinen da draußen. Ihre Endianness kann während der Ausführung variieren. AUCH gibt es nicht nur Little Endian und Big Endian, sondern auch andere Endiannessen (was für ein Wort).
Ich hasse und liebe es gleichzeitig zu programmieren ...
quelle
Deklarieren Sie eine int-Variable:
Verwenden Sie nun char * -Zeiger auf verschiedene Teile davon und überprüfen Sie, was sich in diesen Teilen befindet.
Je nachdem, welches auf 0xFF Byte zeigt, können Sie Endianness erkennen. Dies erfordert sizeof (int)> sizeof (char), gilt aber definitiv für die besprochenen Plattformen.
quelle
Weitere Informationen finden Sie in diesem Codeprojekt-Artikel Grundlegende Konzepte zu Endianness :
quelle
Die C ++ - Methode bestand darin, Boost zu verwenden , bei dem Präprozessorprüfungen und Casts in sehr gründlich getesteten Bibliotheken unterteilt sind.
Die Predef-Bibliothek (boost / predef.h) erkennt vier verschiedene Arten von Endianness .
Die Endian Library sollte dem C ++ - Standard unterzogen werden und unterstützt eine Vielzahl von Vorgängen mit endiansensitiven Daten.
Wie in den obigen Antworten angegeben, wird Endianness Teil von c ++ 20 sein.
quelle
Sofern Sie kein Framework verwenden, das auf PPC- und Intel-Prozessoren portiert wurde, müssen Sie bedingte Kompilierungen durchführen, da PPC- und Intel-Plattformen völlig unterschiedliche Hardwarearchitekturen, Pipelines, Busse usw. aufweisen. Dadurch wird der Assembler-Code zwischen diesen völlig unterschiedlich die Zwei.
Gehen Sie wie folgt vor, um Endianness zu finden:
Sie erhalten entweder tempChar als 0x12 oder 0x34, von dem Sie die Endianness kennen.
quelle
stdint.h
undint16_t
zukunftssicher machen, um zu verhindern, dass Short auf einer anderen Plattform anders ist.Ich würde so etwas machen:
In diesem Sinne würden Sie eine zeiteffiziente Funktion erhalten, die die Berechnung nur einmal durchführt.
quelle
Verwenden Sie wie oben angegeben Unionstricks.
Es gibt jedoch nur wenige Probleme mit den oben empfohlenen, insbesondere, dass der nicht ausgerichtete Speicherzugriff für die meisten Architekturen notorisch langsam ist und einige Compiler solche konstanten Prädikate überhaupt nicht erkennen, es sei denn, das Wort ist ausgerichtet.
Da ein bloßer Endian-Test langweilig ist, gibt es hier eine (Vorlagen-) Funktion, die die Eingabe / Ausgabe einer beliebigen Ganzzahl gemäß Ihrer Spezifikation unabhängig von der Host-Architektur umdreht.
Verwendung:
Verwenden Sie zum Konvertieren von einem bestimmten Endian in einen Host:
host = endian(source, endian_of_source)
Verwenden Sie zum Konvertieren von Host-Endian zu gegebenem Endian:
output = endian(hostsource, endian_you_want_to_output)
Der resultierende Code ist so schnell wie das Schreiben von Handassemblierungen auf clang, auf gcc ist er etwas langsamer (ungerollt &, <<, >>, | für jedes Byte), aber immer noch anständig.
quelle
quelle
#define IS_BIGENDIAN() (*((char*) &((int){ 0x00ff })) == (0x00))
Verwenden Sie kein
union
!C ++ erlaubt kein Typ-Punning über
union
s!Das Lesen aus einem Gewerkschaftsfeld, in das nicht das letzte Feld geschrieben wurde, ist undefiniertes Verhalten !
Viele Compiler unterstützen dies als Erweiterung, aber die Sprache übernimmt keine Garantie.
Siehe diese Antwort für weitere Details:
https://stackoverflow.com/a/11996970
Es gibt nur zwei gültige Antworten, die garantiert portabel sind.
Die erste Antwort, wenn Sie Zugriff auf ein System haben, das C ++ 20 unterstützt,
ist die Verwendung
std::endian
über den<type_traits>
Header.(Zum Zeitpunkt des Schreibens war C ++ 20 noch nicht veröffentlicht. Sofern jedoch nichts die
std::endian
Aufnahme beeinflusst, ist dies die bevorzugte Methode, um die Endianness zur Kompilierungszeit ab C ++ 20 zu testen.)Ab C ++ 20
Vor C ++ 20 besteht die einzig gültige Antwort darin, eine Ganzzahl zu speichern und dann ihr erstes Byte durch Typ Punning zu überprüfen.
Im Gegensatz zur Verwendung von
union
s ist dies vom C ++ - Typsystem ausdrücklich zulässig.Es ist auch wichtig zu bedenken, dass für eine optimale Portabilität
static_cast
verwendet werden sollte,da
reinterpret_cast
die Implementierung definiert ist.Ab C ++ 11
Ab C ++ 11 (ohne Aufzählung)
C ++ 98 / C ++ 03
quelle
Dies ist eine andere Lösung. Ähnlich wie bei Andrew Hares Lösung.
quelle
ungetestet, aber in meinen Augen sollte das funktionieren? weil es 0x01 auf Little Endian und 0x00 auf Big Endian sein wird?
quelle
Erklären:
Mein erster Beitrag wurde fälschlicherweise als "Kompilierungszeit" deklariert. Es ist nicht, es ist sogar im aktuellen C ++ - Standard unmöglich. Der constexpr bedeutet NICHT, dass die Funktion immer zur Kompilierungszeit berechnet. Vielen Dank an Richard Hodges für die Korrektur.
Kompilierungszeit, Nicht-Makro, C ++ 11 Constexpr-Lösung:
quelle
Sie können dies auch über den Präprozessor tun, indem Sie eine Boost-Header-Datei verwenden, die Boost-Endian enthält
quelle
Sofern der Endian-Header nicht nur GCC ist, enthält er Makros, die Sie verwenden können.
quelle
__BYTE_ORDER__
,__ORDER_LITTLE_ENDIAN__
und__ORDER_BIG_ENDIAN__
?Wenn Sie keine bedingte Kompilierung wünschen, können Sie einfach endianunabhängigen Code schreiben. Hier ist ein Beispiel (von Rob Pike ):
Lesen einer in Little-Endian auf der Festplatte gespeicherten Ganzzahl auf endianunabhängige Weise:
Der gleiche Code, der versucht, die Endianness der Maschine zu berücksichtigen:
quelle
quelle
Wie wäre es damit?
quelle
Hier ist eine weitere C-Version. Es definiert ein Makro, das
wicked_cast()
für Inline-Typ-Punning über C99-Union-Literale und den nicht standardmäßigen__typeof__
Operator aufgerufen wird .Wenn Ganzzahlen Einzelbytewerte sind, macht Endianness keinen Sinn und es wird ein Fehler bei der Kompilierung generiert.
quelle
Die Art und Weise, wie C-Compiler (zumindest alle, die ich kenne) arbeiten, muss zur Kompilierungszeit festgelegt werden. Selbst für Biendian-Prozessoren (wie ARM und MIPS) müssen Sie bei der Kompilierung Endianness auswählen. Darüber hinaus ist die Endianität in allen gängigen Dateiformaten für ausführbare Dateien (z. B. ELF) definiert. Obwohl es möglich ist, einen binären Blob aus biandischem Code zu erstellen (für einige ARM-Server-Exploits vielleicht?), Muss dies wahrscheinlich in der Assembly erfolgen.
quelle
Wie von Coriiander hervorgehoben, werden die meisten (wenn nicht alle) dieser Codes hier zur Kompilierungszeit optimiert, sodass die generierten Binärdateien zur Laufzeit nicht die "Endianness" überprüfen.
Es wurde beobachtet, dass eine bestimmte ausführbare Datei nicht in zwei verschiedenen Bytereihenfolgen ausgeführt werden sollte, aber ich habe keine Ahnung, ob dies immer der Fall ist, und es scheint mir ein Hack zu sein, der beim Kompilieren nachschaut. Also habe ich diese Funktion codiert:
MinGW konnte diesen Code nicht optimieren, obwohl es die anderen Codes hier entfernt optimiert. Ich glaube, das liegt daran, dass ich den "zufälligen" Wert, der im kleineren Bytespeicher zugewiesen wurde, unverändert lasse (mindestens 7 seiner Bits), sodass der Compiler nicht wissen kann, was dieser zufällige Wert ist, und nicht optimiert die Funktion weg.
Ich habe die Funktion auch so codiert, dass die Prüfung nur einmal durchgeführt wird und der Rückgabewert für die nächsten Tests gespeichert wird.
quelle
0x7FE
? Warummalloc()
überhaupt verwenden? das ist verschwenderisch. Und_BE
wenn ein (wenn auch kleiner) Speicherverlust und eine Race-Bedingung darauf warten, dass sie eintreten, sind die Vorteile des dynamischen Zwischenspeicherns des Ergebnisses die Mühe nicht wert. Ich würde stattdessen etwas Ähnliches tun:static const uint16_t teste = 1; int is_little_endian() { return (0x01 == ((uint8_t*)&teste)[0]); } int is_big_endian() { return (0x01 == ((uint8_t*)&teste)[1]); }
Einfach und effektiv und viel weniger Arbeit zur Laufzeit.volatile
oder#pragma
usw.Es gibt zwar keine schnelle und standardmäßige Methode, um dies zu bestimmen, dies gibt es jedoch aus:
quelle
Siehe Abbildung Endianness - C-Level Code.
quelle
Ich habe das Lehrbuch durchgesehen: Computersystem: Die Perspektive eines Programmierers , und es gibt ein Problem, festzustellen, welcher Endian dies durch das C-Programm ist.
Ich habe die Funktion des Zeigers verwendet, um dies wie folgt zu tun:
Da der int 4 Bytes und char nur 1 Byte belegt. Wir könnten einen Zeichenzeiger verwenden , um auf das int mit dem Wert 1 zu zeigen. Wenn der Computer also Little Endian ist, hat das Zeichen , auf das der Zeichenzeiger zeigt, den Wert 1, andernfalls sollte sein Wert 0 sein.
quelle