tutorial - hallo welt c++




Endanness programmatisch in einem C++ Programm erkennen (18)

Bitte lesen Sie diesen Artikel :

Hier ist ein Code, um zu bestimmen, was der Typ Ihrer Maschine ist

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

Gibt es einen programmatischen Weg, um zu erkennen, ob Sie sich auf einer Big-Endian- oder Little-Endian-Architektur befinden? Ich muss in der Lage sein, Code zu schreiben, der auf einem Intel- oder PPC-System ausgeführt wird und denselben Code verwendet (dh keine bedingte Kompilierung).


Deklarieren Sie eine int-Variable:

int variable = 0xFF;

Verwenden Sie nun char * -Zeiger 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, welcher Punkt nun auf 0xFF Byte zeigt, kann man die Endianität erkennen. Dies erfordert sizeof (int)> sizeof (char), aber es gilt definitiv für die besprochenen Plattformen.


Die C ++ - Methode war die Verwendung von boost , bei der Präprozessor-Prüfungen und Umwandlungen in sehr gründlich getesteten Bibliotheken kompartimentiert sind.

Die Predef-Bibliothek (boost / predef.h) erkennt vier verschiedene Arten von Endian .

Die Endian-Bibliothek sollte dem C ++ - Standard entsprechen und eine Vielzahl von Operationen für Endian-sensitive Daten unterstützen.

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


Dies geschieht normalerweise zur Kompilierzeit (speziell aus Leistungsgründen), indem Sie die vom Compiler verfügbaren Header-Dateien verwenden oder eigene erstellen. Unter Linux haben Sie die Header-Datei "/usr/include/endian.h"


Für weitere Details, können Sie diesen Code-Projekt Artikel Grundlegende Konzepte auf Endianness überprüfen:

Wie kann ich den Endian-Typ zur Laufzeit dynamisch testen?

Wie in FAQ zu Computeranimationen erläutert, können Sie mithilfe der folgenden Funktion feststellen, ob Ihr Code auf einem Little- oder Big-Endian-System ausgeführt wird: Reduzieren

#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 Ganzzahlwerts zu zeigen. Wenn das erste Byte der Ganzzahl 0x01h ist, dann ist das System Little-Endian (die 0x01h ist in der niedrigsten oder niedrigstwertigen Adresse). Wenn es 0x00h ist, dann ist das System Big-Endian.


Hier ist eine andere C-Version. Es definiert ein Makro namens wicked_cast() für Inline-Typ-Punning über C99-Union-Literale und den Nicht-Standard-Operator __typeof__ .

#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 Integer Single-Byte-Werte sind, ist Endianness nicht sinnvoll und es wird ein Kompilierungsfehler generiert.


Ich mag die Methode nicht, die auf dem Typ punning basiert - gegen den Compiler wird oft gewarnt. Genau dafür sind Gewerkschaften da!

int 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 Typ-Fall, aber dies ist klarer - und ist gemäß C99 garantiert korrekt. gcc bevorzugt dies im Vergleich zum direkten Zeiger-Cast.

Dies ist auch viel besser, als die Endianess zur Kompilierzeit zu fixieren - für OS, die Multiarchitektur unterstützen (fette Binärdatei auf Mac os x zum Beispiel), wird dies sowohl für ppc / i386 funktionieren, während es sehr einfach ist, Dinge anderweitig zu vermasseln .


Ich würde so etwas tun:

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

In diesem Sinne erhalten Sie eine zeitsparende Funktion, die nur einmal rechnet.


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

Sie können std::endian wenn Sie Zugriff auf C ++ 20 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
}

Sie können es tun, indem Sie ein int setzen und Bits abmasken, aber wahrscheinlich ist der einfachste Weg, nur die eingebauten Netzwerk-Byte-Umwandlungs-Ops zu verwenden (da die Netzwerk-Byte-Reihenfolge immer Big-Endian ist).

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

Etwas herumfummeln könnte schneller sein, aber dieser Weg ist einfach, unkompliziert und ziemlich unmöglich zu vermasseln.


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

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. aufweisen die Zwei.

Um Endian zu finden, mach folgendes:

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

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


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

Lesen einer Ganzzahl, die in Little-Endian auf der Festplatte gespeichert ist, auf endian-unabhängige Weise:

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

Derselbe Code, der versucht, die Endlichkeit 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

Wie von Coriiander gezeigt, werden die meisten (wenn nicht alle) dieser Codes zur Kompilierzeit wegoptimiert, so dass die generierten Binärdateien die "Endianness" zur Laufzeit nicht überprüfen.

Es wurde beobachtet, dass eine bestimmte ausführbare Datei nicht in zwei verschiedenen Byte-Reihenfolgen laufen sollte, aber ich habe keine Ahnung, ob das immer der Fall ist, und es scheint mir ein Hack zu sein, der zur Kompilierungszeit überprüft. 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 er die anderen Codes hier optimiert. Ich glaube, das liegt daran, dass ich den "zufälligen" Wert, der dem kleineren Byte-Speicher zugeordnet war, belasse (mindestens 7 seiner Bits), so dass der Compiler nicht wissen kann, was dieser zufällige Wert ist und er nicht optimiert die Funktion weg.

Ich habe die Funktion auch so codiert, dass die Überprüfung nur einmal durchgeführt wird und der Rückgabewert für die nächsten Tests gespeichert wird.


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

bool isBigEndian()
{
    static const uint16_t m_endianCheck(0x00ff);
    return ( *((uint8_t*)&m_endianCheck) == 0x0); 
}

int i=1;
char *c=(char*)&i;
bool littleendian=c;




endianness