Rilevamento dell'endianità a livello di codice in un programma C ++


211

Esiste un modo programmatico per rilevare se ci si trova su un'architettura big-endian o little-endian? Devo essere in grado di scrivere codice che verrà eseguito su un sistema Intel o PPC e utilizzare esattamente lo stesso codice (ovvero nessuna compilazione condizionale).


4
Per completezza, ecco un link alla domanda di qualcun altro sul tentativo di valutare l'endianness (al momento della compilazione): stackoverflow.com/questions/280162/…
Faisal Vali,

14
Perché non determinare l'endianità in fase di compilazione? Non può assolutamente cambiare in fase di esecuzione.
effimero

3
AFAIK, non esiste un modo affidabile e universale per farlo. gcc.gnu.org/ml/gcc-help/2007-07/msg00342.html
user48956

Risposte:


174

Non mi piace il metodo basato sul tipo di punzonatura, che verrà spesso messo in guardia dal compilatore. Questo è esattamente ciò che i sindacati sono per!

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

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

Il principio è equivalente al caso tipo come suggerito da altri, ma questo è più chiaro - e secondo C99, è garantito per essere corretto. gcc preferisce questo rispetto al cast puntatore diretto.

Questo è anche molto meglio che correggere l'endianità al momento della compilazione - per i sistemi operativi che supportano la multiarchitettura (binary fat su Mac os x per esempio), funzionerà sia per ppc / i386, sia che sia molto facile confondere le cose altrimenti .


51
Non consiglio di nominare una variabile "bint" :)
mkb

42
sei sicuro che sia ben definito? In C ++ solo un membro del sindacato può essere attivo alla volta - cioè non puoi assegnare usando un nome-membro e leggere usando un altro (anche se c'è un'eccezione per le strutture compatibili con il layout)
Faisal Vali,

27
@Matt: ho cercato in Google, e bint sembra avere un significato in inglese di cui non ero a conoscenza :)
David Cournapeau,

17
Ho provato questo e in gcc 4.0.1 e gcc 4.4.1 il risultato di questa funzione può essere determinato al momento della compilazione e trattato come una costante. Ciò significa che il compilatore verrà eliminato se i rami dipendono esclusivamente dal risultato di questa funzione e non verranno mai presi sulla piattaforma in questione. Questo probabilmente non è vero per molte implementazioni di Htonl.
Onnipotente,

6
Questa soluzione è davvero portatile? E se CHAR_BIT != 8?
Zorgit,

80

Puoi farlo impostando un int e mascherando i bit, ma probabilmente il modo più semplice è solo quello di utilizzare le operazioni di conversione dei byte di rete integrate (poiché l'ordine dei byte di rete è sempre big endian).

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

La manipolazione dei bit potrebbe essere più veloce, ma in questo modo è semplice, diretto e piuttosto impossibile da confondere.


1
Le operazioni di conversione della rete possono anche essere utilizzate per convertire tutto in big endian, risolvendo così altri problemi che Jay potrebbe incontrare.
Brian,

6
@sharptooth - slow è un termine relativo, ma sì, se la velocità è davvero un problema, usalo una volta all'inizio del programma e imposta una variabile globale con l'endianness.
Eric Petroelje,

5
htonl ha un altro problema: su alcune piattaforme (windows?), non risiede nella libreria di runtime C corretta, ma in ulteriori librerie relative alla rete (socket, ecc ...). Questo è piuttosto un ostacolo per una sola funzione se non hai bisogno della libreria altrimenti.
David Cournapeau,

7
Si noti che su Linux (gcc), htonl è soggetto a ripiegamento costante al momento della compilazione, quindi un'espressione di questo modulo non ha alcun sovraccarico di runtime (cioè è piegato costantemente su 1 o 0, quindi l'eliminazione del codice morto rimuove il altra branca
dell'if

2
Inoltre, su x86 htonl può essere (ed è, su Linux / gcc) implementato in modo molto efficiente usando un assemblatore inline, in particolare se si prende di mira una microarchitettura con supporto per l' BSWAPoperazione.
bdonlan,

61

Vedi questo articolo :

Ecco un po 'di codice per determinare qual è il tipo di macchina

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

25
Tieni presente che dipende dal fatto che int e char sono diverse lunghezze, il che è quasi sempre il caso, ma non è garantito.
David Thornley,

10
Ho lavorato su sistemi embedded in cui int e char short avevano le stesse dimensioni ... Non ricordo se int fosse anche quella dimensione (2 byte) o meno.
rmeador,

2
perché QUESTA risposta è praticamente L'UNICA RISPOSTA che NON mi sta facendo pensare "amico, che cosa stai facendo?", che è il caso della maggior parte delle risposte qui: o
hanshenrik,

2
@Shillard int deve essere almeno così grande, ma nello standard non è richiesto che il char sia limitato a meno! Se dai un'occhiata alla famiglia TI F280x, scoprirai che CHAR_BIT è 16 e sizeof (int) == sizeof (char) mentre i limiti che menzioni sono mantenuti assolutamente bene ...
Aconcagua

5
Perché non usare uint8_t e uint16_t?
Rodrigo,

58

Puoi usarlo std::endianse hai accesso al compilatore C ++ 20 come GCC 8+ o Clang 7+.

Nota: è std::endianiniziato <type_traits>ma è stato spostato alla <bit>riunione di Colonia del 2019. GCC 8, Clang 7, 8 e 9 ce l'hanno <type_traits>mentre GCC 9+ e Clang 10+ ce l'hanno <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
}

5
Come tutti ho accesso a bozze / proposte in C ++ 17 e 20, ma al momento esiste mai un compilatore C ++ 20?
Xeverous,

@Xeverous Richiede solo enumerazioni con ambito, quindi sospetto che la maggior parte dei fornitori lo aggiungerà alla loro implementazione stdlib come una delle loro precedenti modifiche.
Pharap,

@Xeverous GCC 8 è stato rilasciato e lo supporta.
Lyberta

Delle oltre 30 risposte alla domanda, questa sembra essere l'unica, completamente accurata (con un'altra risposta che è almeno corretta in parte).
Indispensabile il

40

Questo viene normalmente eseguito al momento della compilazione (specialmente per motivi di prestazioni) utilizzando i file di intestazione disponibili dal compilatore o crearne uno proprio. Su Linux hai il file di intestazione "/usr/include/endian.h"


8
Non posso credere che questo non sia stato votato più in alto. Non è che l'endianness cambierà con un programma compilato, quindi non c'è mai bisogno di un test di runtime.
Dolda2000,

@ Dolda2000 Potrebbe potenzialmente, vedere le modalità endian ARM.
Tyzoid,

10
@Tyzoid: No, un programma compilato verrà sempre eseguito in modalità endian per cui è stato compilato, anche se il processore è in grado di farlo.
Dolda 2000

16

Ho sorpreso che nessuno abbia menzionato le macro che il pre-processore definisce per impostazione predefinita. Mentre questi varieranno a seconda della tua piattaforma; sono molto più puliti rispetto a dover scrivere il tuo endian-check.

Per esempio; se osserviamo le macro integrate definite da GCC (su una macchina X86-64):

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

Su una macchina PPC ottengo:

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

(La :| gcc -dM -E -x c -magia stampa tutte le macro incorporate).


7
Queste macro non vengono visualizzate in modo coerente. Ad esempio, in gcc 4.4.5 dal repository Redhat 6, l'esecuzione echo "\n" | gcc -x c -E -dM - |& grep -i 'endian'non restituisce nulla, mentre gcc 3.4.3 ( /usr/sfw/bincomunque) in Solaris ha una definizione in tal senso. Ho visto problemi simili su VxWorks Tornado (gcc 2.95) -vs- VxWorks Workbench (gcc 3.4.4).
Brian Vandenberg,

15

Ehm ... Mi sorprende che nessuno abbia capito che il compilatore ottimizzerà semplicemente il test e metterà un risultato fisso come valore di ritorno. Questo rende tutti gli esempi di codice sopra, effettivamente inutili. L'unica cosa che verrebbe restituita è l'endianness in fase di compilazione! E sì, ho testato tutti gli esempi sopra. Ecco un esempio con MSVC 9.0 (Visual Studio 2008).

Codice C puro

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

Smontaggio

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

Forse è possibile disattivare QUALSIASI ottimizzazione in fase di compilazione solo per questa funzione, ma non lo so. Altrimenti è forse possibile codificarlo nell'assemblaggio, anche se non è portatile. E anche allora anche questo potrebbe essere ottimizzato. Mi fa pensare che ho bisogno di un assemblatore davvero schifoso, implementare lo stesso codice per tutte le CPU / set di istruzioni esistenti, e beh ... non importa.

Inoltre, qualcuno qui ha detto che l'endianness non cambia durante il runtime. SBAGLIATO. Ci sono macchine bi-endian là fuori. La loro endianness può variare durante l'esecuzione. Inoltre, non c'è solo Little Endian e Big Endian, ma anche altri endiannesses (che parola).

Odio e amo programmare allo stesso tempo ...


11
Non devi ricompilare per eseguire comunque su una piattaforma diversa?
Bobobobo,

2
Sebbene funzioni bene con MSVC, non lo è per tutte le versioni di GCC in tutte le circostanze. Pertanto, un "controllo del tempo di esecuzione" all'interno di un ciclo critico può essere correttamente non ramificato in fase di compilazione o meno. Non esiste una garanzia al 100%.
Ciano,

21
Non esiste un processore x86 big-endian. Anche se esegui Ubuntu su un processore biendiano (come ARM o MIPS), gli eseguibili ELF sono sempre endian big (MSB) o little (LSB). Non è possibile creare eseguibili biendiani quindi non sono necessari controlli di runtime.
Fabel,

4
Per disattivare l'ottimizzazione in questo metodo usa 'unione volatile ...' Dice al compilatore che 'u' può essere cambiato altrove e che i dati devono essere caricati
mishmashru

1
Affinché questa funzione ritorni un valore diverso in fase di esecuzione rispetto all'ottimizzatore, sta calcolando che ciò implica che l'ottimizzatore è stato corretto. Stai dicendo che ci sono esempi di codice binario ottimizzato compilato che può essere eseguito in modo portabile su due diverse architetture di diversa endianness, nonostante ovvie ipotesi fatte dall'ottimizzatore (durante il programma) durante la compilazione che sembrerebbero incompatibili con almeno una di quelle architetture?
Scott,

13

Dichiarare una variabile int:

int variable = 0xFF;

Ora usa i puntatori char * su varie parti di esso e controlla cosa c'è in quelle parti.

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

A seconda di quale punta a byte 0xFF ora puoi rilevare l'endianness. Ciò richiede sizeof (int)> sizeof (char), ma è sicuramente vero per le piattaforme discusse.


8

Per ulteriori dettagli, ti consigliamo di dare un'occhiata a questo articolo codeproject Concetti di base su Endianness :

Come testare dinamicamente il tipo di Endian in fase di esecuzione?

Come spiegato nelle Domande frequenti sull'animazione al computer, puoi utilizzare la seguente funzione per vedere se il tuo codice è in esecuzione su un sistema Little o Big Endian: 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);
}

Questo codice assegna il valore 0001h a un numero intero a 16 bit. Un puntatore a carattere viene quindi assegnato per puntare al primo byte (meno significativo) del valore intero. Se il primo byte dell'intero è 0x01h, il sistema è Little-Endian (lo 0x01h è nell'indirizzo più basso o meno significativo). Se è 0x00h, il sistema è Big-Endian.


6

Il modo C ++ è stato quello di utilizzare boost , in cui i controlli e i cast dei preprocessori sono suddivisi in compartimenti all'interno di librerie molto testate.

La libreria Predef (boost / predef.h) riconosce quattro diversi tipi di endianness .

La biblioteca di Endian doveva essere sottoposta allo standard C ++ e supporta una vasta gamma di operazioni su dati sensibili agli endiani.

Come indicato nelle risposte sopra, Endianness farà parte di c ++ 20.


1
Cordiali saluti, il collegamento "quattro diversi tipi di endianness" è interrotto,
Remy Lebeau,

wiki riparato e reso
fuzzyTew

5

A meno che non si stia utilizzando un framework che è stato portato su processori PPC e Intel, sarà necessario eseguire compilazioni condizionali, poiché le piattaforme PPC e Intel hanno architetture hardware, pipeline, bus, ecc. Completamente diverse, il che rende il codice assembly completamente diverso tra il due.

Per quanto riguarda la ricerca di endianness, procedi come segue:

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

Otterrai tempChar come 0x12 o 0x34, da cui conoscerai l'endianness.


3
Ciò si basa sul fatto che in breve sono esattamente 2 byte che non sono garantiti.
sharptooth,

3
Sarebbe una scommessa abbastanza sicura, sebbene basata sulle due architetture fornite nella domanda.
Daemin,

8
Includere stdint.he utilizzare int16_tper la prova futura contro corto essere diversi su un'altra piattaforma.
Denise Skidmore,

4

Vorrei fare qualcosa del genere:

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

Lungo queste linee, otterresti una funzione efficiente in termini di tempo che esegue il calcolo una sola volta.


puoi incorporarlo? non sono sicuro che in linea causi più blocchi di memoria delle variabili statiche
aah134

4

Come detto sopra, usa i trucchi dell'Unione.

Tuttavia, ci sono alcuni problemi con quelli sopra indicati, in particolare che l'accesso alla memoria non allineato è notoriamente lento per la maggior parte delle architetture e alcuni compilatori non riconoscono nemmeno tali predicati costanti, a meno che le parole non siano allineate.

Poiché il semplice test endian è noioso, ecco la funzione (template) che inverte l'input / output dell'intero arbitrario in base alle vostre specifiche, indipendentemente dall'architettura host.

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

Uso:

Per convertire da un dato endian a host, utilizzare:

host = endian(source, endian_of_source)

Per convertire da endian host a endian dato, utilizzare:

output = endian(hostsource, endian_you_want_to_output)

Il codice risultante è veloce come scrivere l'assemblaggio della mano su clang, su gcc è un po 'più lento (srotolato e, <<, >>, | per ogni byte) ma comunque decente.


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

1
Sarebbe equivalente? #define IS_BIGENDIAN() (*((char*) &((int){ 0x00ff })) == (0x00))
Emanuel,

4

Non usare a union!

C ++ non consente la punzonatura di tipo tramite unions!
Leggere da un campo sindacale che non era l'ultimo campo scritto è un comportamento indefinito !
Molti compilatori lo supportano come estensioni, ma la lingua non offre alcuna garanzia.

Vedi questa risposta per maggiori dettagli:

https://stackoverflow.com/a/11996970


Ci sono solo due risposte valide che sono garantite come portatili.

La prima risposta, se si ha accesso a un sistema che supporta C ++ 20,
è utilizzare std::endiandall'intestazione <type_traits>.

(Al momento in cui scrivo, C ++ 20 non è ancora stato rilasciato, ma a meno che non accada qualcosa che influisca std::endiansull'inclusione, questo sarà il modo preferito per testare l'endianità al momento della compilazione da C ++ 20 in poi.)

C ++ 20 in poi

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

Prima di C ++ 20, l'unica risposta valida è quella di memorizzare un numero intero e quindi ispezionare il suo primo byte tramite la punzonatura del tipo.
A differenza dell'uso di unions, ciò è espressamente consentito dal sistema di tipi di C ++.

È anche importante ricordare che per la portabilità ottimale static_castdovrebbe essere usato,
perché reinterpret_castè definita l'implementazione.

Se un programma tenta di accedere al valore memorizzato di un oggetto attraverso un valore diverso da uno dei seguenti tipi, il comportamento non è definito: ... a charo unsigned chartipo.

C ++ 11 in poi

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

C ++ 11 in poi (senza enum)

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

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");

Questa è un'altra soluzione. Simile alla soluzione di Andrew Hare.


3

non testato, ma secondo me dovrebbe funzionare? perché sarà 0x01 su little endian e 0x00 su big endian?

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

3

Dichiarare:

Il mio post iniziale è erroneamente dichiarato "tempo di compilazione". Non lo è, è addirittura impossibile nell'attuale standard C ++. Constexpr NON significa che la funzione esegue sempre calcoli in fase di compilazione. Grazie Richard Hodges per la correzione.

tempo di compilazione, non macro, soluzione constexpr C ++ 11:

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

constexpr bool is_little_endian() {
  return d.c[0] == 1;
}

2
C'è un motivo particolare per cui hai usato il carattere unsigned su uint8_t?
Kevin,

0 sovraccarico di runtime ... mi piace!
Hanshenrik,

Immagino che questo rilevi endiannes della build machine, non il target?
hutorny,

2
Questo UB non è in C ++?
rr-

6
questo non è legale nel contesto constexpr. Non è possibile accedere a un membro di un sindacato che non è stato inizializzato direttamente. Non c'è modo di rilevare legalmente l'endianità al momento della compilazione senza la magia del preprocessore.
Richard Hodges,

2

Puoi anche farlo tramite il preprocessore usando qualcosa come il file di intestazione boost che puoi trovare boost endian


1

A meno che l'intestazione endian non sia solo GCC, fornisce macro che è possibile utilizzare.

#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!");
...

Non sono questi __BYTE_ORDER__, __ORDER_LITTLE_ENDIAN__e __ORDER_BIG_ENDIAN__?
Xeverous,

1

Se non si desidera la compilazione condizionale, è possibile semplicemente scrivere codice indipendente endian. Ecco un esempio (tratto da Rob Pike ):

Leggere un numero intero memorizzato in little-endian su disco, in modo indipendente da endian:

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

Lo stesso codice, cercando di prendere in considerazione l'endianness della macchina:

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

Che bella idea! E ora trasferiamo i tuoi numeri interi tramite socket di rete su un dispositivo sconosciuto.
Maksym Ganenko,

@MaksymGanenko Non capisco il tuo commento. È ironia? Io non suggerendo di non specificare endianness dei dati serializzati. Sto suggerendo di non scrivere codice dipendente dall'endianness della macchina che riceve i dati.
fjardon,

@MaksymGanenko Se decidi di votare, potresti spiegare perché la risposta è sbagliata. Come minimo per aiutare i potenziali lettori a capire perché non dovrebbero seguire la mia risposta.
fjardon,


0

Cosa ne pensi di questo?

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

0

Ecco un'altra versione C. Definisce una macro chiamata wicked_cast()per la punzonatura di tipo inline tramite valori letterali dell'Unione C99 e l' __typeof__operatore non standard .

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

Se i numeri interi sono valori a byte singolo, l'endianness non ha senso e verrà generato un errore di compilazione.


0

Il modo in cui i compilatori C (almeno tutti quelli che conosco) funzionano l'endianità deve essere deciso al momento della compilazione. Anche per i processori biendiani (come ARM och MIPS) devi scegliere l'endianness in fase di compilazione. Inoltre, l'endianness è definita in tutti i formati di file comuni per gli eseguibili (come ELF). Sebbene sia possibile creare un BLOB binario di codice biandiano (forse per qualche exploit del server ARM?), Probabilmente deve essere fatto in assembly.


-1

Come sottolineato da Coriiander, la maggior parte (se non tutti) di questi codici verrà ottimizzata in fase di compilazione, quindi i binari generati non controlleranno "endianness" in fase di esecuzione.

È stato osservato che un determinato file eseguibile non dovrebbe essere eseguito in due diversi ordini di byte, ma non ho idea se sia sempre così, e mi sembra un trucco da controllare al momento della compilazione. Quindi ho codificato questa funzione:

#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 non è stato in grado di ottimizzare questo codice, anche se ottimizza gli altri codici qui. Credo che sia perché lascio il valore "casuale" che era allocato nella memoria byte più piccola com'era (almeno 7 dei suoi bit), quindi il compilatore non può sapere quale sia quel valore casuale e non ottimizza la funzione di distanza.

Ho anche codificato la funzione in modo che il controllo venga eseguito una sola volta e il valore restituito venga memorizzato per i test successivi.


Perché allocare 4 byte per lavorare su un valore di 2 byte? Perché mascherare un valore indeterminato con 0x7FE? Perché usare malloc()affatto? è dispendioso. Ed _BEè una (anche se piccola) perdita di memoria e una condizione di competizione in attesa di accadere, i vantaggi della memorizzazione nella cache del risultato in modo dinamico non valgono il problema. Invece farei qualcosa di più simile a questo: 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]); }semplice ed efficace e molto meno lavoro da eseguire in fase di esecuzione.
Remy Lebeau,

@RemyLebeau, il punto centrale della mia risposta è stato quello di produrre un codice che non è stato ottimizzato dal compilatore. Certo, il tuo codice è molto più semplice, ma con le ottimizzazioni attivate diventerà un booleano costante dopo la compilazione. Come ho affermato nella mia risposta, in realtà non so se esiste un modo per compilare il codice C in modo che lo stesso eseguibile venga eseguito su entrambi gli ordini di byte ed ero anche curioso di vedere se potevo effettuare il controllo in fase di esecuzione nonostante le ottimizzazioni siano attive.
Tex Killer

@TexKiller quindi perché non semplicemente disabilitare le ottimizzazioni per il codice? Usando volatile, o #pragma, ecc.
Remy Lebeau,

@RemyLebeau, all'epoca non conoscevo quelle parole chiave e l'ho solo presa come una piccola sfida per impedire l'ottimizzazione del compilatore con quello che sapevo.
Tex Killer,

-1

mentre non esiste un modo rapido e standard per determinarlo, questo lo produrrà:

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

-1

Vedi Endianness - Illustrazione del codice C-Level.

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

-2

Stavo sfogliando il libro di testo: Sistema informatico: la prospettiva di un programmatore , e c'è un problema nel determinare quale endian è questo dal programma C.

Ho usato la funzione del puntatore per farlo come segue:

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

Poiché int occupa 4 byte e char occupa solo 1 byte. Potremmo usare un puntatore char per indicare l' int con valore 1. Quindi, se il computer è un piccolo endian, il carattere a cui punta il puntatore char è con valore 1, altrimenti il ​​suo valore dovrebbe essere 0.


questo sarebbe migliorato usando int32t.
shuttle87,

1
^ se vuoi nitpick, il migliore qui è int16_fast_t. e l'attuale codice di @ Archimedes520 non funzionerà su un arco in cui int è nativamente int8;) (che potrebbe andare contro gli standard c in primo luogo, comunque)
hanshenrik,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.