Endianness programmgesteuert in einem C ++ - Programm erkennen

211

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).

Jay T.
quelle
4
Der Vollständigkeit halber ist hier ein Link zu einer anderen Frage zum Versuch, die Endianness (zur Kompilierungszeit) zu messen
Faisal Vali
14
Warum nicht die Endianness zur Kompilierungszeit bestimmen? Es kann sich unmöglich zur Laufzeit ändern.
Ephemient
3
AFAIK, es gibt keinen zuverlässigen und universellen Weg, dies zu tun. gcc.gnu.org/ml/gcc-help/2007-07/msg00342.html
user48956

Antworten:

174

Ich mag die Methode, die auf Typ Punning basiert, nicht - sie wird oft vom Compiler gewarnt. Genau dafür sind Gewerkschaften da!

bool is_big_endian(void)
{
    union {
        uint32_t i;
        char c[4];
    } bint = {0x01020304};

    return bint.c[0] == 1; 
}

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 .

David Cournapeau
quelle
51
Ich empfehle nicht, eine Variable "bint" zu nennen :)
mkb
42
Sind Sie sicher, dass dies gut definiert ist? In C ++ kann immer nur ein Mitglied der Gewerkschaft gleichzeitig aktiv sein - dh Sie können nicht mit einem Mitgliedsnamen zuweisen und mit einem anderen lesen (obwohl es eine Ausnahme für layoutkompatible Strukturen gibt)
Faisal Vali
26
@Matt: Ich habe in Google geschaut und bint scheint eine Bedeutung auf Englisch zu haben, die mir nicht bekannt war :)
David Cournapeau
17
Ich habe dies getestet und sowohl in gcc 4.0.1 als auch in gcc 4.4.1 kann das Ergebnis dieser Funktion zur Kompilierungszeit ermittelt und als Konstante behandelt werden. Dies bedeutet, dass der Compiler gelöscht wird, wenn Verzweigungen, die ausschließlich vom Ergebnis dieser Funktion abhängen und niemals auf der betreffenden Plattform verwendet werden. Dies gilt wahrscheinlich nicht für viele Implementierungen von htonl.
Omnifarious
6
Ist diese Lösung wirklich portabel? Was wäre wenn CHAR_BIT != 8?
Zorgit
80

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).

if ( htonl(47) == 47 ) {
  // Big endian
} else {
  // Little endian.
}

Ein bisschen Fummeln könnte schneller sein, aber dieser Weg ist einfach, unkompliziert und ziemlich unmöglich zu vermasseln.

Eric Petroelje
quelle
1
Die Netzwerkkonvertierungsoperationen können auch verwendet werden, um alles in Big Endian zu konvertieren und so andere Probleme zu lösen, auf die Jay möglicherweise stößt.
Brian
6
@sharptooth - langsam ist ein relativer Begriff, aber ja, wenn Geschwindigkeit wirklich ein Problem ist, verwenden Sie ihn einmal zu Beginn des Programms und setzen Sie eine globale Variable mit der Endianness.
Eric Petroelje
5
htonl hat ein weiteres Problem: Auf einigen Plattformen (Windows?) befindet es sich nicht in der eigentlichen C-Laufzeitbibliothek, sondern in zusätzlichen netzwerkbezogenen Bibliotheken (Socket usw.). Dies ist ein ziemliches Hindernis für nur eine Funktion, wenn Sie die Bibliothek sonst nicht benötigen.
David Cournapeau
7
Beachten Sie, dass htonl unter Linux (gcc) zur Kompilierungszeit einer konstanten Faltung unterliegt, sodass ein Ausdruck dieses Formulars überhaupt keinen Laufzeitaufwand hat (dh, er wird konstant auf 1 oder 0 gefaltet, und die Eliminierung von totem Code entfernt das anderer Zweig des if)
bdonlan
2
Auf x86 kann (und wird unter Linux / gcc) htonl auch sehr effizient mit Inline Assembler implementiert werden, insbesondere wenn Sie auf eine Mikroarchitektur mit Unterstützung für den BSWAPVorgang abzielen .
Bdonlan
61

Bitte lesen Sie diesen Artikel :

Hier ist ein Code, um den Typ Ihrer Maschine zu bestimmen

int num = 1;
if(*(char *)&num == 1)
{
    printf("\nLittle-Endian\n");
}
else
{
    printf("Big-Endian\n");
}
Andrew Hare
quelle
25
Denken Sie daran, dass es darauf ankommt, dass int und char unterschiedlich lang sind, was fast immer der Fall ist, aber nicht garantiert wird.
David Thornley
10
Ich habe an eingebetteten Systemen gearbeitet, bei denen short int und char dieselbe Größe hatten ... Ich kann mich nicht erinnern, ob reguläres int auch diese Größe (2 Bytes) hatte oder nicht.
Rmeador
2
Warum ist DIESE Antwort so ziemlich DIE EINZIGE ANTWORT, die mich NICHT zum Nachdenken bringt "Alter, was machst du?", was bei den meisten Antworten hier der Fall ist: o
hanshenrik
2
@Shillard int muss mindestens so groß sein, aber es gibt keine Anforderung im Standard, dass Saibling auf weniger beschränkt ist! Wenn Sie sich die TI F280x-Familie ansehen, werden Sie feststellen, dass CHAR_BIT 16 und sizeof (int) == sizeof (char) ist, während die von Ihnen genannten Grenzwerte absolut in Ordnung sind ...
Aconcagua
5
Warum nicht uint8_t und uint16_t verwenden?
Rodrigo
58

Sie können verwenden, std::endianwenn Sie Zugriff auf C ++ 20-Compiler wie GCC 8+ oder Clang 7+ haben.

Hinweis: std::endianbegann 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>.

#include <bit>

if constexpr (std::endian::native == std::endian::big)
{
    // Big endian system
}
else if constexpr (std::endian::native == std::endian::little)
{
    // Little endian system
}
else
{
    // Something else
}
Lyberta
quelle
5
Wie jeder habe ich Zugriff auf C ++ 17 und 20 Entwürfe / Vorschläge, aber gibt es derzeit jemals einen C ++ 20 Compiler?
Xeverous
@Xeverous Es sind nur Aufzählungen mit Gültigkeitsbereich erforderlich, daher vermute ich, dass die meisten Anbieter es als eine ihrer früheren Änderungen zu ihrer stdlib-Implementierung hinzufügen werden.
Pharap
@Xeverous GCC 8 wurde veröffentlicht und unterstützt es.
Lyberta
Von den über 30 Antworten auf die Frage scheint dies die einzige zu sein, die völlig korrekt ist (mit einer anderen Antwort, die zumindest teilweise richtig ist).
Unsichtbarer
40

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"

Rechnung
quelle
8
Ich kann nicht glauben, dass dies nicht höher gewählt wurde. Es ist nicht so, dass sich die Endianness unter einem kompilierten Programm ändern wird, daher ist kein Laufzeit-Test erforderlich.
Dolda2000
@ Dolda2000 Es könnte möglicherweise die ARM-Endian-Modi sehen.
Tyzoid
10
@ Tyzoid: Nein, ein kompiliertes Programm wird immer im Endian-Modus ausgeführt, für den es kompiliert wurde, auch wenn der Prozessor dazu in der Lage ist.
Dolda2000
16

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):

:| gcc -dM -E -x c - |grep -i endian
#define __LITTLE_ENDIAN__ 1

Auf einer PPC-Maschine bekomme ich:

:| gcc -dM -E -x c - |grep -i endian
#define __BIG_ENDIAN__ 1
#define _BIG_ENDIAN 1

(Die :| gcc -dM -E -x c -Magie druckt alle eingebauten Makros aus).

DaveR
quelle
7
Diese Makros werden überhaupt nicht konsistent angezeigt. In gcc 4.4.5 aus dem Redhat 6-Repo gibt das Ausführen beispielsweise echo "\n" | gcc -x c -E -dM - |& grep -i 'endian'nichts zurück, während gcc 3.4.3 (von /usr/sfw/binsowieso) 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.
Brian Vandenberg
15

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

int32 DNA_GetEndianness(void)
{
    union 
    {
        uint8  c[4];
        uint32 i;
    } u;

    u.i = 0x01020304;

    if (0x04 == u.c[0])
        return DNA_ENDIAN_LITTLE;
    else if (0x01 == u.c[0])
        return DNA_ENDIAN_BIG;
    else
        return DNA_ENDIAN_UNKNOWN;
}

Demontage

PUBLIC  _DNA_GetEndianness
; Function compile flags: /Ogtpy
; File c:\development\dna\source\libraries\dna\endian.c
;   COMDAT _DNA_GetEndianness
_TEXT   SEGMENT
_DNA_GetEndianness PROC                 ; COMDAT

; 11   :     union 
; 12   :     {
; 13   :         uint8  c[4];
; 14   :         uint32 i;
; 15   :     } u;
; 16   : 
; 17   :     u.i = 1;
; 18   : 
; 19   :     if (1 == u.c[0])
; 20   :         return DNA_ENDIAN_LITTLE;

    mov eax, 1

; 21   :     else if (1 == u.c[3])
; 22   :         return DNA_ENDIAN_BIG;
; 23   :     else
; 24   :        return DNA_ENDIAN_UNKNOWN;
; 25   : }

    ret
_DNA_GetEndianness ENDP
END

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 ...

Koriiander
quelle
11
Müssen Sie nicht neu kompilieren, um auf einer anderen Plattform ausgeführt zu werden?
Bobobobo
2
Obwohl es für MSVC gut funktioniert, funktioniert es nicht unter allen Umständen für alle GCC-Versionen. Daher kann eine "Laufzeitprüfung" innerhalb einer kritischen Schleife zur Kompilierungszeit korrekt entzweigt sein oder nicht. Es gibt keine 100% ige Garantie.
Cyan
21
Es gibt keinen Big-Endian-x86-Prozessor. Selbst wenn Sie Ubuntu auf einem Biendian-Prozessor (wie ARM oder MIPS) ausführen, sind die ausführbaren ELF-Dateien immer entweder große (MSB) oder kleine (LSB) Endian. Es können keine ausführbaren Biendian-Dateien erstellt werden, sodass keine Laufzeitprüfungen erforderlich sind.
Fabel
4
Um die Optimierung in dieser Methode zu deaktivieren, verwenden Sie 'volatile union ...'. Es teilt dem Compiler mit, dass 'u' woanders geändert werden kann und Daten geladen werden sollten
mishmashru
1
Damit diese Funktion zur Laufzeit einen anderen Wert als das Optimierungsprogramm zurückgibt, wird berechnet, dass das Optimierungsprogramm fehlerhaft ist. Wollen Sie damit sagen, dass es Beispiele für kompilierten optimierten Binärcode gibt, der portabel auf zwei verschiedenen Architekturen unterschiedlicher Endianness ausgeführt werden kann, obwohl der Optimierer (während des gesamten Programms) während der Kompilierung offensichtliche Annahmen getroffen hat, die mit mindestens einer davon nicht kompatibel zu sein scheinen Architekturen?
Scott
13

Deklarieren Sie eine int-Variable:

int variable = 0xFF;

Verwenden Sie nun char * -Zeiger auf verschiedene Teile davon und überprüfen Sie, was sich in diesen Teilen befindet.

char* startPart = reinterpret_cast<char*>( &variable );
char* endPart = reinterpret_cast<char*>( &variable ) + sizeof( int ) - 1;

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.

scharfer Zahn
quelle
8

Weitere Informationen finden Sie in diesem Codeprojekt-Artikel Grundlegende Konzepte zu Endianness :

Wie kann man zur Laufzeit dynamisch auf den Endian-Typ testen?

Wie in den häufig gestellten Fragen zur Computeranimation erläutert, können Sie mit der folgenden Funktion feststellen, ob Ihr Code auf einem Little- oder Big-Endian-System ausgeführt wird: Collapse

#define BIG_ENDIAN      0
#define LITTLE_ENDIAN   1
int TestByteOrder()
{
   short int word = 0x0001;
   char *byte = (char *) &word;
   return(byte[0] ? LITTLE_ENDIAN : BIG_ENDIAN);
}

Dieser Code weist einer 16-Bit-Ganzzahl den Wert 0001h zu. Ein Zeichenzeiger wird dann zugewiesen, um auf das erste (niedrigstwertige) Byte des ganzzahligen Werts zu zeigen. Wenn das erste Byte der Ganzzahl 0x01h ist, ist das System Little-Endian (das 0x01h befindet sich in der niedrigsten oder niedrigstwertigen Adresse). Wenn es 0x00h ist, ist das System Big-Endian.

keiner
quelle
6

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.

fuzzyTew
quelle
1
Zu Ihrer Information, die Verbindung "vier verschiedene Arten von Endianness" ist unterbrochen.
Remy Lebeau
behoben und gemacht wiki
fuzzyTew
5

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:

short temp = 0x1234;
char* tempChar = (char*)&temp;

Sie erhalten entweder tempChar als 0x12 oder 0x34, von dem Sie die Endianness kennen.

Samoz
quelle
3
Dies setzt voraus, dass Short genau 2 Bytes beträgt, was nicht garantiert ist.
Scharfzahn
3
Aufgrund der beiden in der Frage angegebenen Architekturen wäre dies jedoch eine ziemlich sichere Sache.
Daemin
8
Einbeziehen stdint.hund int16_tzukunftssicher machen, um zu verhindern, dass Short auf einer anderen Plattform anders ist.
Denise Skidmore
4

Ich würde so etwas machen:

bool isBigEndian() {
    static unsigned long x(1);
    static bool result(reinterpret_cast<unsigned char*>(&x)[0] == 0);
    return result;
}

In diesem Sinne würden Sie eine zeiteffiziente Funktion erhalten, die die Berechnung nur einmal durchführt.

Jeremy Mayhew
quelle
kannst du es inline?
Ich bin
4

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.

#include <stdint.h>

#define BIG_ENDIAN 1
#define LITTLE_ENDIAN 0

template <typename T>
T endian(T w, uint32_t endian)
{
    // this gets optimized out into if (endian == host_endian) return w;
    union { uint64_t quad; uint32_t islittle; } t;
    t.quad = 1;
    if (t.islittle ^ endian) return w;
    T r = 0;

    // decent compilers will unroll this (gcc)
    // or even convert straight into single bswap (clang)
    for (int i = 0; i < sizeof(r); i++) {
        r <<= 8;
        r |= w & 0xff;
        w >>= 8;
    }
    return r;
};

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.

kat
quelle
4
bool isBigEndian()
{
    static const uint16_t m_endianCheck(0x00ff);
    return ( *((uint8_t*)&m_endianCheck) == 0x0); 
}
Paolo Brandoli
quelle
1
Wäre das gleichwertig? #define IS_BIGENDIAN() (*((char*) &((int){ 0x00ff })) == (0x00))
Emanuel
4

Verwenden Sie kein union!

C ++ erlaubt kein Typ-Punning über unions!
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::endianAufnahme beeinflusst, ist dies die bevorzugte Methode, um die Endianness zur Kompilierungszeit ab C ++ 20 zu testen.)

Ab C ++ 20

constexpr bool is_little_endian = (std::endian::native == std::endian::little);

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 unions ist dies vom C ++ - Typsystem ausdrücklich zulässig.

Es ist auch wichtig zu bedenken, dass für eine optimale Portabilität static_castverwendet werden sollte,
da reinterpret_castdie Implementierung definiert ist.

Wenn ein Programm versucht, über einen anderen Wert als einen der folgenden Typen auf den gespeicherten Wert eines Objekts zuzugreifen, ist das Verhalten undefiniert: ... a charoder unsigned charTyp.

Ab C ++ 11

enum class endianness
{
    little = 0,
    big = 1,
};

inline endianness get_system_endianness()
{
    const int value { 0x01 };
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01) ? endianness::little : endianness::big;
}

Ab C ++ 11 (ohne Aufzählung)

inline bool is_system_little_endian()
{
    const int value { 0x01 };
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01);
}

C ++ 98 / C ++ 03

inline bool is_system_little_endian()
{
    const int value = 0x01;
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01);
}
Pharap
quelle
3
union {
    int i;
    char c[sizeof(int)];
} x;
x.i = 1;
if(x.c[0] == 1)
    printf("little-endian\n");
else    printf("big-endian\n");

Dies ist eine andere Lösung. Ähnlich wie bei Andrew Hares Lösung.

Neeraj
quelle
3

ungetestet, aber in meinen Augen sollte das funktionieren? weil es 0x01 auf Little Endian und 0x00 auf Big Endian sein wird?

bool runtimeIsLittleEndian(void)
{
 volatile uint16_t i=1;
 return  ((uint8_t*)&i)[0]==0x01;//0x01=little, 0x00=big
}
hanshenrik
quelle
3

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:

union {
  uint16_t s;
  unsigned char c[2];
} constexpr static  d {1};

constexpr bool is_little_endian() {
  return d.c[0] == 1;
}
zhaorufei
quelle
2
Gibt es einen bestimmten Grund, warum Sie vorzeichenloses Zeichen über uint8_t verwendet haben?
Kevin
0 Laufzeit Overhead ... ich mag es!
Hanshenrik
Ich denke, dies erkennt Endiannes der Build-Maschine, nicht das Ziel?
Hutorny
2
Ist das nicht UB in C ++?
rr-
6
Dies ist im Kontext nicht legal. Sie können nicht auf ein Mitglied einer Gewerkschaft zugreifen, das nicht direkt initialisiert wurde. Ohne Präprozessor-Magie gibt es keine Möglichkeit, Endianness zur Kompilierungszeit legal zu erkennen.
Richard Hodges
1

Sofern der Endian-Header nicht nur GCC ist, enthält er Makros, die Sie verwenden können.

#include "endian.h"
...
if (__BYTE_ORDER == __LITTLE_ENDIAN) { ... }
else if (__BYTE_ORDER == __BIG_ENDIAN) { ... }
else { throw std::runtime_error("Sorry, this version does not support PDP Endian!");
...
Mark A. Libby
quelle
Ist diese nicht __BYTE_ORDER__, __ORDER_LITTLE_ENDIAN__und __ORDER_BIG_ENDIAN__?
Xeverous
1

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:

i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);

Der gleiche Code, der versucht, die Endianness der Maschine zu berücksichtigen:

i = *((int*)data);
#ifdef BIG_ENDIAN
/* swap the bytes */
i = ((i&0xFF)<<24) | (((i>>8)&0xFF)<<16) | (((i>>16)&0xFF)<<8) | (((i>>24)&0xFF)<<0);
#endif
Fjardon
quelle
Was für eine schöne Idee! Und jetzt übertragen wir Ihre Ganzzahlen über den Netzwerk-Socket auf ein unbekanntes Gerät.
Maksym Ganenko
@MaksymGanenko Ich verstehe deinen Kommentar nicht. Ist es Ironie? Ich bin nicht darauf hindeutet , nicht endianness von serialisierten Daten angeben. Ich schlage vor, keinen Code zu schreiben, der von der Endianness der Maschine abhängt, die die Daten empfängt.
Fjardon
@MaksymGanenko Wenn Sie abstimmen, können Sie erklären, warum die Antwort falsch ist. Zumindest, um potenziellen Lesern zu helfen, zu verstehen, warum sie meiner Antwort nicht folgen sollten.
Fjardon
0
int i=1;
char *c=(char*)&i;
bool littleendian=c;
Jon Bright
quelle
0

Wie wäre es damit?

#include <cstdio>

int main()
{
    unsigned int n = 1;
    char *p = 0;

    p = (char*)&n;
    if (*p == 1)
        std::printf("Little Endian\n");
    else 
        if (*(p + sizeof(int) - 1) == 1)
            std::printf("Big Endian\n");
        else
            std::printf("What the crap?\n");
    return 0;
}
Abhay
quelle
0

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 .

#include <limits.h>

#if UCHAR_MAX == UINT_MAX
#error endianness irrelevant as sizeof(int) == 1
#endif

#define wicked_cast(TYPE, VALUE) \
    (((union { __typeof__(VALUE) src; TYPE dest; }){ .src = VALUE }).dest)

_Bool is_little_endian(void)
{
    return wicked_cast(unsigned char, 1u);
}

Wenn Ganzzahlen Einzelbytewerte sind, macht Endianness keinen Sinn und es wird ein Fehler bei der Kompilierung generiert.

Christoph
quelle
0

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.

Fabel
quelle
-1

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:

#include <stdint.h>

int* _BE = 0;

int is_big_endian() {
    if (_BE == 0) {
        uint16_t* teste = (uint16_t*)malloc(4);
        *teste = (*teste & 0x01FE) | 0x0100;
        uint8_t teste2 = ((uint8_t*) teste)[0];
        free(teste);
        _BE = (int*)malloc(sizeof(int));
        *_BE = (0x01 == teste2);
    }
    return *_BE;
}

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.

Tex Killer
quelle
Warum 4 Bytes zuweisen, um an einem 2-Byte-Wert zu arbeiten? Warum einen unbestimmten Wert mit maskieren 0x7FE? Warum malloc()überhaupt verwenden? das ist verschwenderisch. Und _BEwenn 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.
Remy Lebeau
@RemyLebeau, der springende Punkt meiner Antwort war, einen Code zu erstellen, der vom Compiler nicht optimiert wird. Sicher, Ihr Code ist viel einfacher, aber mit aktivierten Optimierungen wird er nach dem Kompilieren nur zu einem konstanten Booleschen Wert. Wie ich in meiner Antwort angegeben habe, weiß ich nicht, ob es eine Möglichkeit gibt, C-Code so zu kompilieren, dass dieselbe ausführbare Datei in beiden Bytereihenfolgen ausgeführt wird, und ich war auch gespannt, ob ich die Überprüfung zur Laufzeit durchführen kann trotz Optimierungen.
Tex Killer
@TexKiller Warum nicht einfach Optimierungen für den Code deaktivieren? Verwenden von volatileoder #pragmausw.
Remy Lebeau
@RemyLebeau, ich kannte diese Schlüsselwörter damals nicht und habe es nur als kleine Herausforderung angesehen, die Compileroptimierung mit dem zu verhindern, was ich wusste.
Tex Killer
-1

Es gibt zwar keine schnelle und standardmäßige Methode, um dies zu bestimmen, dies gibt es jedoch aus:

#include <stdio.h> 
int main()  
{ 
   unsigned int i = 1; 
   char *c = (char*)&i; 
   if (*c)     
       printf("Little endian"); 
   else
       printf("Big endian"); 
   getchar(); 
   return 0; 
} 
Yekanchi
quelle
-1

Siehe Abbildung Endianness - C-Level Code.

// assuming target architecture is 32-bit = 4-Bytes
enum ENDIANNESS{ LITTLEENDIAN , BIGENDIAN , UNHANDLE };


ENDIANNESS CheckArchEndianalityV1( void )
{
    int Endian = 0x00000001; // assuming target architecture is 32-bit    

    // as Endian = 0x00000001 so MSB (Most Significant Byte) = 0x00 and LSB (Least     Significant Byte) = 0x01
    // casting down to a single byte value LSB discarding higher bytes    

    return (*(char *) &Endian == 0x01) ? LITTLEENDIAN : BIGENDIAN;
} 
gimel
quelle
-2

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:

#include <stdio.h>

int main(void){
    int i=1;
    unsigned char* ii = &i;

    printf("This computer is %s endian.\n", ((ii[0]==1) ? "little" : "big"));
    return 0;
}

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.

Archimedes520
quelle
Dies würde durch die Verwendung von int32t verbessert.
Shuttle87
1
^ Wenn Sie nicht auswählen möchten, ist das Beste hier int16_fast_t. und @ Archimedes520s aktueller Code funktioniert nicht auf einem Bogen, auf dem int nativ int8 ist;) (das könnte jedoch in erster Linie gegen die c-Standards
verstoßen