Makrodefinition zur Ermittlung von Big-Endian- oder Little-Endian-Maschine?

Lesezeit: 8 Minuten

Benutzeravatar von manav mn
manav mn

Gibt es eine einzeilige Makrodefinition, um die Endianness der Maschine zu bestimmen? Ich verwende den folgenden Code, aber das Konvertieren in ein Makro wäre zu lang:

unsigned char test_endian( void )
{
    int test_var = 1;
    unsigned char *test_endian = (unsigned char*)&test_var;

    return (test_endian[0] == 0);
}

  • Warum nicht denselben Code in ein Makro einfügen?

    – scharfer Zahn

    20. Januar 2010 um 9:46 Uhr

  • Sie können die Endianness nicht allein mit dem C-Präprozessor portabel bestimmen. Sie wollen auch 0 Anstatt von NULL in Ihrem Abschlusstest, und ändern Sie eine der test_endian Objekte zu etwas anderem :-).

    – Alok Singhal

    20. Januar 2010 um 9:48 Uhr


  • Und warum ist ein Makro notwendig? Die Inline-Funktion würde dasselbe tun und ist viel sicherer.

    – scharfer Zahn

    20. Januar 2010 um 9:49 Uhr

  • @Sharptooth, ein Makro ist ansprechend, weil sein Wert zur Kompilierzeit bekannt sein kann, was bedeutet, dass Sie die Endianness Ihrer Plattform verwenden können, um beispielsweise die Vorlageninstanziierung zu steuern, oder vielleicht sogar verschiedene Codeblöcke mit einem auswählen können #if Richtlinie.

    – Rob Kennedy

    8. April 2010 um 5:08 Uhr


  • Das stimmt, aber ineffizient. Wenn ich eine Little-Endian-CPU habe und Little-Endian-Daten in die Leitung oder in eine Datei schreibe, würde ich es lieber vermeiden, Daten sinnlos zu entpacken und neu zu packen. Früher habe ich beruflich Videotreiber geschrieben. es ist äußerst wichtig beim Schreiben von Pixeln auf eine Grafikkarte, um jeden Ort zu optimieren, an dem Sie können.

    – Eduard Falk

    5. September 2016 um 16:26 Uhr

Benutzeravatar von Christoph
Christoph

Code, der willkürliche Byte-Reihenfolgen unterstützt und bereit ist, in eine Datei namens abgelegt zu werden order32.h:

#ifndef ORDER32_H
#define ORDER32_H

#include <limits.h>
#include <stdint.h>

#if CHAR_BIT != 8
#error "unsupported char size"
#endif

enum
{
    O32_LITTLE_ENDIAN = 0x03020100ul,
    O32_BIG_ENDIAN = 0x00010203ul,
    O32_PDP_ENDIAN = 0x01000302ul,      /* DEC PDP-11 (aka ENDIAN_LITTLE_WORD) */
    O32_HONEYWELL_ENDIAN = 0x02030001ul /* Honeywell 316 (aka ENDIAN_BIG_WORD) */
};

static const union { unsigned char bytes[4]; uint32_t value; } o32_host_order =
    { { 0, 1, 2, 3 } };

#define O32_HOST_ORDER (o32_host_order.value)

#endif

Sie würden über nach Little-Endian-Systemen suchen

O32_HOST_ORDER == O32_LITTLE_ENDIAN

  • Das lässt dich nicht sich entscheiden Endian-ness bis zur Laufzeit. Folgendes kann nicht kompiliert werden, weil. /** isLittleEndian::result –> 0 oder 1 */ struct isLittleEndian { enum isLittleEndianResult { result = (O32_HOST_ORDER == O32_LITTLE_ENDIAN) }; };

    – Benutzer48956

    13. August 2010 um 17:54 Uhr

  • Ist es unmöglich, das Ergebnis bis zur Laufzeit zu erhalten?

    – k06a

    26. Dezember 2010 um 12:03 Uhr

  • Warum char? Besser verwenden uint8_t und schlagen fehl, wenn dieser Typ nicht verfügbar ist (was überprüft werden kann durch #if UINT8_MAX). Beachten Sie, dass CHAR_BIT ist unabhängig von uint8_t.

    – Andreas Spindler

    31. Oktober 2012 um 11:33 Uhr


  • Dies ist UB in c++: stackoverflow.com/questions/11373203/…

    Benutzer3624760

    1. Juli 2016 um 8:55 Uhr

  • Lassen Sie mich der Vollständigkeit halber noch einen in die Mischung werfen: O32_HONEYWELL_ENDIAN = 0x02030001ul /* Honeywell 316 */

    – Eduard Falk

    5. September 2016 um 17:10 Uhr

Benutzeravatar von caf
Café

Wenn Sie einen Compiler haben, der zusammengesetzte C99-Literale unterstützt:

#define IS_BIG_ENDIAN (!*(unsigned char *)&(uint16_t){1})

oder:

#define IS_BIG_ENDIAN (!(union { uint16_t u16; unsigned char c; }){ .u16 = 1 }.c)

Im Allgemeinen sollten Sie jedoch versuchen, Code zu schreiben, der nicht von der Endianness der Hostplattform abhängt.


Beispiel für eine Host-Endianness-unabhängige Implementierung von ntohl():

uint32_t ntohl(uint32_t n)
{
    unsigned char *np = (unsigned char *)&n;

    return ((uint32_t)np[0] << 24) |
        ((uint32_t)np[1] << 16) |
        ((uint32_t)np[2] << 8) |
        (uint32_t)np[3];
}

  • “Sie sollten versuchen, Code zu schreiben, der nicht von der Endianness der Host-Plattform abhängt”. Leider stieß mein Appell “Ich weiß, dass wir eine POSIX-Kompatibilitätsschicht schreiben, aber ich möchte ntoh nicht implementieren, weil es von der Endianness der Host-Plattform abhängt” immer auf taube Ohren gestoßen ;-). Die Verarbeitung von Grafikformaten und Konvertierungscode ist der andere Hauptkandidat, den ich gesehen habe – Sie möchten nicht alles darauf stützen, ständig ntohl anzurufen.

    – Steve Jessop

    20. Januar 2010 um 13:03 Uhr


  • Sie können umsetzen ntohl auf eine Weise, die nicht von der Endianness der Host-Plattform abhängt.

    – Café

    20. Januar 2010 um 13:13 Uhr

  • @caf wie würdest du ntohl auf host-endianness-unabhängige Weise schreiben?

    – Hayri Uğur Koltuk

    1. März 2012 um 12:39 Uhr

  • @AliVeli: Ich habe der Antwort eine Beispielimplementierung hinzugefügt.

    – Café

    1. März 2012 um 21:12 Uhr

  • Ich sollte auch fürs Protokoll hinzufügen, dass “(*(uint16_t *)”\0\xff” < 0x100)" nicht in eine Konstante kompiliert wird, egal wie viel ich optimiere, zumindest mit gcc 4.5.2. Es erstellt immer ausführbaren Code.

    – Eduard Falk

    11. Juli 2012 um 20:29 Uhr

Es gibt keinen Standard, aber auf vielen Systemen darunter <endian.h> gibt Ihnen einige Definitionen, nach denen Sie suchen können.

  • Testen Sie die Endianness mit #if __BYTE_ORDER == __LITTLE_ENDIAN und #elif __BYTE_ORDER == __BIG_ENDIAN. Und erzeuge ein #error sonst.

    – To1ne

    4. Mai 2011 um 7:43 Uhr


  • <endian.h> ist unter Windows nicht verfügbar

    – rostig

    2. November 2016 um 15:27 Uhr

  • Android und Chrom Projekte verwenden endian.h wenn nicht __APPLE__ oder _WIN32 ist definiert.

    – patryk.beza

    11. November 2016 um 13:42 Uhr


  • In OpenBSD 6.3 stellt bereit #if BYTE_ORDER == LITTLE_ENDIAN(oder BIG_ENDIAN) ohne Unterstriche vor den Namen. _BYTE_ORDER ist nur für Systemkopfzeilen. __BYTE_ORDER ist nicht vorhanden.

    – Georg Köhler

    6. April 2018 um 4:11 Uhr


  • @To1ne Ich bezweifle, dass Endianness für Windows relevant ist, da Windows (zumindest derzeit) nur auf x86- und ARM-Computern läuft. x86 ist immer LE und ARM ist konfigurierbar, um beide Architekturen zu verwenden.

    – SimonC

    19. Dezember 2018 um 8:13 Uhr

Um Endianness zur Laufzeit zu erkennen, müssen Sie in der Lage sein, auf den Speicher zu verweisen. Wenn Sie sich an Standard-C halten, erfordert die Deklaration einer Variablen im Speicher eine Anweisung, aber die Rückgabe eines Werts erfordert einen Ausdruck. Ich weiß nicht, wie ich das in einem einzelnen Makro machen soll – deshalb hat gcc Erweiterungen 🙂

Wenn Sie bereit sind, eine .h-Datei zu haben, können Sie diese definieren

static uint32_t endianness = 0xdeadbeef; 
enum endianness { BIG, LITTLE };

#define ENDIANNESS ( *(const char *)&endianness == 0xef ? LITTLE \
                   : *(const char *)&endianness == 0xde ? BIG \
                   : assert(0))

und dann kannst du die verwenden ENDIANNESS Makro wie Sie wollen.

Benutzeravatar von Gregory Pakosz
Gregor Pakosz

Wenn Sie sich nur auf den Präprozessor verlassen wollen, müssen Sie die Liste der vordefinierten Symbole herausfinden. Präprozessor-Arithmetik hat kein Adressierungskonzept.

GCC auf Mac definiert __LITTLE_ENDIAN__ oder __BIG_ENDIAN__

$ gcc -E -dM - < /dev/null |grep ENDIAN
#define __LITTLE_ENDIAN__ 1

Dann können Sie weitere bedingte Präprozessor-Direktiven basierend auf der Plattformerkennung wie hinzufügen #ifdef _WIN32 usw.

  • GCC 4.1.2 unter Linux scheint diese Makros nicht zu definieren, obwohl GCC 4.0.1 und 4.2.1 sie auf Macintosh definieren. Es ist also keine zuverlässige Methode für die plattformübergreifende Entwicklung, selbst wenn Sie bestimmen dürfen, welcher Compiler verwendet werden soll.

    – Rob Kennedy

    8. April 2010 um 3:02 Uhr

  • Oh ja, es liegt daran, dass es nur von GCC auf dem Mac definiert wird.

    – Gregory Pakosz

    8. August 2011 um 2:18 Uhr

  • Hinweis: Mein GCC (auf Mac) definiert #define __BIG_ENDIAN__ 1 und #define _BIG_ENDIAN 1.

    Benutzer1985657

    28. September 2014 um 16:16 Uhr

  • clang 5.0.1 für OpenBSD/amd64 hat #define __LITTLE_ENDIAN__ 1. Dieses Makro scheint eine Clang-Funktion zu sein, keine gcc-Funktion. Das gcc Der Befehl auf einigen Macs ist nicht gcc, sondern clang.

    – Georg Köhler

    6. April 2018 um 4:05 Uhr

  • GCC 4.2.1 auf Mac war damals GCC

    – Gregory Pakosz

    6. April 2018 um 11:30 Uhr

Benutzeravatar von ggpp23
ggpp23

Ich glaube, danach wurde gefragt. Ich habe dies nur auf einer kleinen Endian-Maschine unter msvc getestet. Jemand bestätigt bitte auf einer Big-Endian-Maschine.

    #define LITTLE_ENDIAN 0x41424344UL 
    #define BIG_ENDIAN    0x44434241UL
    #define PDP_ENDIAN    0x42414443UL
    #define ENDIAN_ORDER  ('ABCD') 

    #if ENDIAN_ORDER==LITTLE_ENDIAN
        #error "machine is little endian"
    #elif ENDIAN_ORDER==BIG_ENDIAN
        #error "machine is big endian"
    #elif ENDIAN_ORDER==PDP_ENDIAN
        #error "jeez, machine is PDP!"
    #else
        #error "What kind of hardware is this?!"
    #endif

Als Nebenbemerkung (Compiler-spezifisch) können Sie mit einem aggressiven Compiler die Optimierung “Dead Code Elimination” verwenden, um den gleichen Effekt wie eine Kompilierzeit zu erzielen #if so:

    unsigned yourOwnEndianSpecific_htonl(unsigned n)
    {
        static unsigned long signature= 0x01020304UL; 
        if (1 == (unsigned char&)signature) // big endian
            return n;
        if (2 == (unsigned char&)signature) // the PDP style
        {
            n = ((n << 8) & 0xFF00FF00UL) | ((n>>8) & 0x00FF00FFUL);
            return n;
        }
        if (4 == (unsigned char&)signature) // little endian
        {
            n = (n << 16) | (n >> 16);
            n = ((n << 8) & 0xFF00FF00UL) | ((n>>8) & 0x00FF00FFUL);
            return n;
        }
        // only weird machines get here
        return n; // ?
    }

Das Obige beruht auf der Tatsache, dass der Compiler die konstanten Werte zur Kompilierzeit erkennt und den darin enthaltenen Code vollständig entfernt if (false) { ... } und ersetzt Code wie if (true) { foo(); } mit foo(); Das Worst-Case-Szenario: Der Compiler führt die Optimierung nicht durch, Sie erhalten immer noch korrekten Code, aber etwas langsamer.

  • GCC 4.1.2 unter Linux scheint diese Makros nicht zu definieren, obwohl GCC 4.0.1 und 4.2.1 sie auf Macintosh definieren. Es ist also keine zuverlässige Methode für die plattformübergreifende Entwicklung, selbst wenn Sie bestimmen dürfen, welcher Compiler verwendet werden soll.

    – Rob Kennedy

    8. April 2010 um 3:02 Uhr

  • Oh ja, es liegt daran, dass es nur von GCC auf dem Mac definiert wird.

    – Gregory Pakosz

    8. August 2011 um 2:18 Uhr

  • Hinweis: Mein GCC (auf Mac) definiert #define __BIG_ENDIAN__ 1 und #define _BIG_ENDIAN 1.

    Benutzer1985657

    28. September 2014 um 16:16 Uhr

  • clang 5.0.1 für OpenBSD/amd64 hat #define __LITTLE_ENDIAN__ 1. Dieses Makro scheint eine Clang-Funktion zu sein, keine gcc-Funktion. Das gcc Der Befehl auf einigen Macs ist nicht gcc, sondern clang.

    – Georg Köhler

    6. April 2018 um 4:05 Uhr

  • GCC 4.2.1 auf Mac war damals GCC

    – Gregory Pakosz

    6. April 2018 um 11:30 Uhr

Benutzeravatar von Jérôme Pouiller
Jérôme Pouiller

Wenn Sie nach einem Kompilierzeittest suchen und gcc verwenden, können Sie Folgendes tun:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__

Sehen gcc-Dokumentation für mehr Informationen.

  • Dies ist definitiv die beste Antwort für alle, die gcc verwenden

    – rtpax

    21. Juli 2017 um 13:27 Uhr

  • __BYTE_ORDER__ ist seit GCC 4.6 verfügbar

    – Benoît Blanchon

    30. März 2018 um 7:17 Uhr


1422260cookie-checkMakrodefinition zur Ermittlung von Big-Endian- oder Little-Endian-Maschine?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy