web-dev-qa-db-de.com

Endianness programmgesteuert in einem C++ - Programm erkennen

Gibt es eine programmatische Methode, um zu ermitteln, 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 verwendet (d. H. Keine bedingte Kompilierung).

183
Jay T

Ich mag die Methode nicht, die auf dem Typ Punning basiert - 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 ist dem von anderen vorgeschlagenen Typfall gleichwertig, aber das ist klarer - und gemäß C99 ist es garantiert korrekt. gcc bevorzugt dies gegenüber der direkten Zeigerumwandlung.

Dies ist auch viel besser als die Festlegung des Endianness zur Kompilierzeit. Für Betriebssysteme, die Multi-Architekturen unterstützen (z. B. fette Binärdateien unter Mac OS X), wird dies für beide Seiten von ppc/i386 funktionieren, während es sonst sehr leicht ist, die Dinge ansonsten durcheinander zu bringen .

161

Sie können dies tun, indem Sie ein int setzen und Bits ausmaskieren. Die einfachste Möglichkeit ist jedoch, die eingebauten Netzwerkbyteumwandlungsoperationen zu verwenden (da die Netzwerkbyteordnung 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.

77
Eric Petroelje

Bitte sehen Sie diesen Artikel :

Hier ist etwas Code, um zu bestimmen, was .__ ist. der Typ Ihrer Maschine

int num = 1;
if(*(char *)&num == 1)
{
    printf("\nLittle-Endian\n");
}
else
{
    printf("Big-Endian\n");
}
57
Andrew Hare

Dies geschieht normalerweise zur Kompilierzeit (besonders aus Performance-Gründen), indem die im Compiler verfügbaren Header-Dateien verwendet oder eigene erstellt werden. Unter Linux haben Sie die Header-Datei "/usr/include/endian.h"

32
bill

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

#include <type_traits>

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
}
26
Lyberta

Ähm ... Es überrascht mich, dass niemand erkannt hat, dass der Compiler den Test einfach optimiert und ein festes Ergebnis als Rückgabewert liefert. Dadurch werden alle obigen Codebeispiele praktisch unbrauchbar. Das einzige, was zurückgegeben werden würde, ist die Endianness zur Kompilierzeit! 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

Möglicherweise ist es möglich, die Kompilierzeitoptimierung für nur diese Funktion zu deaktivieren, aber ich weiß es nicht. Andernfalls ist es möglicherweise möglich, sie in Assembly fest zu codieren, obwohl dies nicht portabel ist. Und selbst dann könnte das optimiert werden. Ich denke, ich brauche einen wirklich beschissenen Assembler, den gleichen Code für alle vorhandenen CPUs/Befehlssätze und, na gut, egal.

Jemand sagte hier auch, dass sich Endiananness während der Laufzeit nicht ändert. FALSCH. Es gibt dort bi-endian Maschinen. Ihre Endgültigkeit kann während der Ausführung variieren. AUCH gibt es nicht nur Little Endian und Big Endian, sondern auch andere Endianzen (was für ein Wort).

Ich hasse und liebe es, gleichzeitig zu programmieren ...

15
Coriiander

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 Ihren eigenen Endian-Check schreiben zu müssen. 

Zum Beispiel; Wenn wir uns die eingebauten 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 integrierten Makros aus.).

14
DaveR

Deklarieren Sie eine int-Variable:

int variable = 0xFF;

Verwenden Sie jetzt char * -Punkte für verschiedene Teile und prü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 Byte auf 0xFF-Byte zeigt, können Sie die Endianness erkennen. Dies erfordert sizeof (int)> sizeof (char), gilt jedoch definitiv für die besprochenen Plattformen.

14
sharptooth

Weitere Informationen hierzu finden Sie in diesem Artikel des Codeprojekts Grundlegende Konzepte zu Endianness :

Wie wird der Endian-Typ zur Laufzeit dynamisch getestet?

Wie in Computer Erläutert. Animations-FAQ können Sie die .__ verwenden. folgende Funktion, um zu sehen, ob Ihr Code läuft auf einem Little- oder Big-Endian System: Zusammenbruch

#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 einem .__ den Wert 0001h zu. 16-Bit-Ganzzahl Ein Zeichenzeiger ist dann zugewiesen auf den ersten (niederwertigstes) Byte des Integer Wert. Wenn das erste Byte von die ganze Zahl ist 0x01h, dann das System ist Little-Endian (0x01h steht in der niedrigsten oder niedrigstwertigen Adresse). Wenn es 0x00h ist, dann die System ist Big-Endian.

7
none

Der C++ - Weg war die Verwendung von boost , bei der Präprozessorprüfungen und -umwandlungen in sehr sorgfältig getesteten Bibliotheken unterteilt sind.

Die Predef Library (boost/predef.h) erkennt vier verschiedene Arten von Endianness .

Die Endian Library sollte dem C++ - Standard übergeben werden und unterstützt eine Vielzahl von Operationen für sensible Daten.

Wie in den Antworten oben erwähnt, wird Endianness Teil von c ++ 20 sein.

6
fuzzyTew

Wenn 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. haben Dadurch wird der Assembly-Code zwischen den beiden völlig unterschiedlich.

Um Endianness zu finden, gehen Sie wie folgt vor:

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

Sie erhalten entweder TempChar als 0x12 oder 0x34, von der Sie die Endianness kennen.

5
samoz

Wie oben erwähnt, verwende Unionstricks.

Es gibt jedoch nur wenige Probleme mit den oben genannten, insbesondere, dass der unausgerichtete Speicherzugriff für die meisten Architekturen notorisch langsam ist, und einige Compiler erkennen solche konstanten Prädikate überhaupt nicht, es sei denn, Word ist ausgerichtet.

Da der reine 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;
};

Verwendungszweck:

Verwenden Sie zum Konvertieren von gegebenem Endian zu Host:

Host = endian(source, endian_of_source)

Verwenden Sie zum Konvertieren von Host-Endian zu gegebenem Endian Folgendes:

output = endian(hostsource, endian_you_want_to_output)

Der resultierende Code ist so schnell wie das Schreiben von hand Assembly auf clang, auf gcc etwas langsamer (nicht gerollt &, <<, >>, | für jedes Byte), aber immer noch anständig.

5
kat
bool isBigEndian()
{
    static const uint16_t m_endianCheck(0x00ff);
    return ( *((uint8_t*)&m_endianCheck) == 0x0); 
}
4
Paolo Brandoli

kompilierzeit, 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;
}
4
zhaorufei

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;
}

Auf diese Weise erhalten Sie eine zeiteffiziente Funktion, die die Berechnung nur einmal durchführt. 

4
Jeremy Mayhew

ungeprüft, aber in meinem Kopf sollte das funktionieren? weil es auf Little Endian 0x01 und auf Big Endian 0x00 sein wird?

bool runtimeIsLittleEndian(void)
{
 volatile uint16_t i=1;
 return  ((uint8_t*)&i)[0]==0x01;//0x01=little, 0x00=big
}
3
hanshenrik
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 der Lösung von Andrew Hare.

3
Neeraj

Sie können dies auch über den Präprozessor mit einer Boost-Header-Datei tun, die gefunden werden kann boost endian

2
nmushell

Wenn der Endian-Header 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!");
...
1
Mark A. Libby

Wenn Sie keine bedingte Kompilierung wünschen, können Sie einfach endian-unabhängigen Code schreiben. Hier ist ein Beispiel (aus Rob Pike ):

Eine Ganzzahl, die in Little Endian auf der Festplatte gespeichert ist, wird auf unabhängige Weise gelesen:

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

Derselbe 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
1
fjardon
int i=1;
char *c=(char*)&i;
bool littleendian=c;
1
Jon Bright

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;
}
1
Abhay

Die Art und Weise, in der C-Compiler (zumindest alle, die ich kenne) die Endannahme hat bearbeiten, die zur Kompilierzeit festgelegt wird. Selbst für biendianische Prozessoren (wie ARM und MIPS) müssen Sie beim Compilieren die Endianness auswählen. Darüber hinaus wird die Endianness in allen gängigen Dateiformaten für ausführbare Dateien (z. B. ELF) definiert. Obwohl es möglich ist, einen binären Blob aus biandianischem Code zu erstellen (für einige ARM Server-Exploite?), Muss er wahrscheinlich in Assembly ausgeführt werden.

0
Fabel

Siehe Endianness - C-Level-Code-Abbildung.

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


ENDIANESS 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;
} 
0
gimel

Keine union verwenden!

C++ erlaubt kein Punning von Typen über unions!
Lesen von einem Union-Feld, das nicht das letzte Feld war, in das geschrieben wurde, ist undefiniertes Verhalten !
Viele Compiler unterstützen dies als Erweiterung, die Sprache gibt jedoch keine Garantie.

Weitere Informationen finden Sie in dieser Antwort:

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,
soll std::endian aus dem <type_traits>-Header verwenden.

(Zum Zeitpunkt des Schreibens wurde C++ 20 noch nicht veröffentlicht. Sofern sich jedoch etwas auf die Aufnahme von std::endian auswirkt, ist dies die bevorzugte Methode, um die Endianness ab C++ 20 zur Kompilierungszeit 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 das erste Byte durch Typ-Punning zu überprüfen.
Im Gegensatz zur Verwendung von unions ist dies im Typensystem von C++ ausdrücklich erlaubt.

Es ist auch wichtig zu bedenken, dass für eine optimale Portabilität static_cast verwendet werden sollte.
weil reinterpret_cast implementierungsdefiniert 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: ... ein Typ char oder unsigned char.

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);
}
0
Pharap

Hier ist eine andere C-Version. Es definiert ein Makro mit dem Namen wicked_cast() für Inline-Typ-Punning über C99-Unionsliterale und den nicht standardmäßigen __typeof__-Operator.

#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 Einzelbyte-Werte sind, macht endianness keinen Sinn und es wird ein Fehler bei der Kompilierung generiert.

0
Christoph

es gibt zwar keine schnelle und standardisierte Methode, um dies zu bestimmen, dies wird jedoch ausgegeben:

#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; 
} 
0
yekanchi