Come faccio a convertire tra valori big-endian e little-endian in C ++?

Come faccio a convertire tra valori big-endian e little-endian in C ++?

EDIT: per chiarezza, devo tradurre i dati binari (valori in virgola mobile a precisione doppia e interi 32-bit e 64-bit) da un’architettura CPU a un’altra. Ciò non implica il networking, quindi le funzioni ntoh () e simili non funzioneranno qui.

EDIT # 2: La risposta che ho accettato si applica direttamente ai compilatori che sto studiando (ecco perché l’ho scelto). Tuttavia, ci sono altre risposte molto buone e più portatili qui.

Se stai usando Visual C ++ fai quanto segue: includi intrin.h e chiama le seguenti funzioni:

Per numeri a 16 bit:

 unsigned short _byteswap_ushort(unsigned short value); 

Per numeri a 32 bit:

 unsigned long _byteswap_ulong(unsigned long value); 

Per numeri a 64 bit:

 unsigned __int64 _byteswap_uint64(unsigned __int64 value); 

Non è necessario convertire i numeri a 8 bit (caratteri).

Inoltre, questi sono definiti solo per valori non firmati che funzionano anche per interi con segno.

Per i float e i double è più difficile come con gli interi semplici, poiché questi possono o no essere nell’ordine dei byte delle macchine host. Puoi far galleggiare little-endian su macchine big-endian e viceversa.

Anche altri compilatori hanno intrinsecamente simili.

Ad esempio, in GCC puoi chiamare direttamente:

 int32_t __builtin_bswap32 (int32_t x) int64_t __builtin_bswap64 (int64_t x) 

(non c’è bisogno di includere qualcosa). Afaik bits.h dichiara la stessa funzione anche in un modo non gcc-centric.

Lo scambio di 16 bit è solo un po ‘ruotare.

Chiamare gli elementi intrinseci invece di far ruotare i propri dati offre le migliori prestazioni e la densità del codice btw ..

In poche parole:

 #include  template  T swap_endian(T u) { static_assert (CHAR_BIT == 8, "CHAR_BIT != 8"); union { T u; unsigned char u8[sizeof(T)]; } source, dest; source.u = u; for (size_t k = 0; k < sizeof(T); k++) dest.u8[k] = source.u8[sizeof(T) - k - 1]; return dest.u; } 

utilizzo: swap_endian(42) .

Da The Byte Order Fallacy di Rob Pyke:

Supponiamo che il tuo stream di dati abbia un numero intero a 32 bit con codifica little-endian. Ecco come estrarlo (assumendo byte senza segno):

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

Se è big-endian, ecco come estrarlo:

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

TL; DR: non preoccuparti per l'ordine nativo della tua piattaforma, tutto ciò che conta è l'ordine dei byte del stream da cui stai leggendo e spero che sia ben definito.

Nota: è stato osservato nel commento che la conversione di tipo esplicito assente, era importante che i data fossero un array di unsigned char o uint8_t . Utilizzando il signed char o il char firmato (se firmato) i data[x] verranno promossi a un numero intero e i data[x] << 24 potenzialmente spostando un 1 nel bit del segno che è UB.

Se stai facendo questo per scopi di compatibilità di rete / host dovresti usare:

 ntohl() //Network to Host byte order (Long) htonl() //Host to Network byte order (Long) ntohs() //Network to Host byte order (Short) htons() //Host to Network byte order (Short) 

Se lo fai per qualche altro motivo, una delle soluzioni byte_swap qui presentate funzionerebbe perfettamente.

Ho preso alcuni suggerimenti da questo post e li ho messi insieme per formare questo:

 #include  #include  #include  #include  enum endianness { little_endian, big_endian, network_endian = big_endian, #if defined(BOOST_LITTLE_ENDIAN) host_endian = little_endian #elif defined(BOOST_BIG_ENDIAN) host_endian = big_endian #else #error "unable to determine system endianness" #endif }; namespace detail { template struct swap_bytes { inline T operator()(T val) { throw std::out_of_range("data size"); } }; template struct swap_bytes { inline T operator()(T val) { return val; } }; template struct swap_bytes { inline T operator()(T val) { return ((((val) >> 8) & 0xff) | (((val) & 0xff) << 8)); } }; template struct swap_bytes { inline T operator()(T val) { return ((((val) & 0xff000000) >> 24) | (((val) & 0x00ff0000) >> 8) | (((val) & 0x0000ff00) << 8) | (((val) & 0x000000ff) << 24)); } }; template<> struct swap_bytes { inline float operator()(float val) { uint32_t mem =swap_bytes()(*(uint32_t*)&val); return *(float*)&mem; } }; template struct swap_bytes { inline T operator()(T val) { return ((((val) & 0xff00000000000000ull) >> 56) | (((val) & 0x00ff000000000000ull) >> 40) | (((val) & 0x0000ff0000000000ull) >> 24) | (((val) & 0x000000ff00000000ull) >> 8 ) | (((val) & 0x00000000ff000000ull) << 8 ) | (((val) & 0x0000000000ff0000ull) << 24) | (((val) & 0x000000000000ff00ull) << 40) | (((val) & 0x00000000000000ffull) << 56)); } }; template<> struct swap_bytes { inline double operator()(double val) { uint64_t mem =swap_bytes()(*(uint64_t*)&val); return *(double*)&mem; } }; template struct do_byte_swap { inline T operator()(T value) { return swap_bytes()(value); } }; // specialisations when attempting to swap to the same endianess template struct do_byte_swap { inline T operator()(T value) { return value; } }; template struct do_byte_swap { inline T operator()(T value) { return value; } }; } // namespace detail template inline T byte_swap(T value) { // ensure the data is only 1, 2, 4 or 8 bytes BOOST_STATIC_ASSERT(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8); // ensure we're only swapping arithmetic types BOOST_STATIC_ASSERT(boost::is_arithmetic::value); return detail::do_byte_swap()(value); } 

C’è un’istruzione di assemblaggio chiamata BSWAP che farà lo scambio per te, estremamente veloce . Puoi leggere a riguardo qui .

Visual Studio, o più precisamente la libreria di runtime di Visual C ++, ha intrinseche delle piattaforms per questo, chiamate _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64() . Simile dovrebbe esistere per altre piattaforms, ma non sono a conoscenza di ciò che verrebbero chiamati.

La procedura per andare da big-endian a little-endian è la stessa che va da little-endian a big-endian.

Ecco alcuni esempi di codice:

 void swapByteOrder(unsigned short& us) { us = (us >> 8) | (us << 8); } void swapByteOrder(unsigned int& ui) { ui = (ui >> 24) | ((ui<<8) & 0x00FF0000) | ((ui>>8) & 0x0000FF00) | (ui << 24); } void swapByteOrder(unsigned long long& ull) { ull = (ull >> 56) | ((ull<<40) & 0x00FF000000000000) | ((ull<<24) & 0x0000FF0000000000) | ((ull<<8) & 0x000000FF00000000) | ((ull>>8) & 0x00000000FF000000) | ((ull>>24) & 0x0000000000FF0000) | ((ull>>40) & 0x000000000000FF00) | (ull << 56); } 

Lo abbiamo fatto con i modelli. Potresti avere qualcosa del genere:

 // Specialization for 2-byte types. template<> inline void endian_byte_swapper< 2 >(char* dest, char const* src) { // Use bit manipulations instead of accessing individual bytes from memory, much faster. ushort* p_dest = reinterpret_cast< ushort* >(dest); ushort const* const p_src = reinterpret_cast< ushort const* >(src); *p_dest = (*p_src >> 8) | (*p_src << 8); } // Specialization for 4-byte types. template<> inline void endian_byte_swapper< 4 >(char* dest, char const* src) { // Use bit manipulations instead of accessing individual bytes from memory, much faster. uint* p_dest = reinterpret_cast< uint* >(dest); uint const* const p_src = reinterpret_cast< uint const* >(src); *p_dest = (*p_src >> 24) | ((*p_src & 0x00ff0000) >> 8) | ((*p_src & 0x0000ff00) << 8) | (*p_src << 24); } 

Se lo fai per trasferire i dati tra piattaforms diverse guarda le funzioni ntoh e hton.

Nello stesso modo in cui fai in C:

 short big = 0xdead; short little = (((big & 0xff)<<8) | ((big & 0xff00)>>8)); 

Si potrebbe anche dichiarare un vettore di caratteri non firmati, memcpy il valore di input in esso, invertire i byte in un altro vettore e memcpy i byte fuori, ma che richiederanno ordini di grandezza più lunghi del bit-twiddling, specialmente con valori a 64 bit.

Sulla maggior parte dei sistemi POSIX (attraverso non è nello standard POSIX) c’è endian.h, che può essere usato per determinare quale codifica utilizza il tuo sistema. Da lì è qualcosa del genere:

 unsigned int change_endian(unsinged int x) { unsigned char *ptr = (unsigned char *)&x; return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]; } 

Scambia l'ordine (da big-endian a little endian):

Se hai il numero 0xDEADBEEF (su un piccolo sistema endian memorizzato come 0xEFBEADDE), ptr [0] sarà 0xEF, ptr [1] è 0xBE, ecc.

Ma se vuoi usarlo per il networking, allora htons, htonl e htonll (e i loro inversi ntoh, ntohl e ntohll) saranno utili per la conversione dall'ordine host all'ordine di rete.

Nota che, almeno per Windows, htonl () è molto più lento della loro controparte intrinseca _byteswap_ulong (). Il primo è una chiamata alla libreria DLL in ws2_32.dll, l’ultima è un’istruzione di assemblaggio BSWAP. Pertanto, se stai scrivendo un codice dipendente dalla piattaforma, preferisci utilizzare l’intrinseco per la velocità:

 #define htonl(x) _byteswap_ulong(x) 

Questo può essere particolarmente importante per l’elaborazione di immagini .PNG in cui tutti gli interi sono salvati in Big Endian con la spiegazione “Si può usare htonl () …” {per rallentare i tipici programmi di Windows, se non si è preparati}.

La maggior parte delle piattaforms ha un file di intestazione di sistema che fornisce funzioni byteswap efficienti. Su Linux è in . Puoi avvolgerlo bene in C ++:

 #include  #include  template struct SizeT {}; #define BYTESWAPS(bits) \ template inline T htobe(T t, SizeT) { return htobe ## bits(t); } \ template inline T htole(T t, SizeT) { return htole ## bits(t); } \ template inline T betoh(T t, SizeT) { return be ## bits ## toh(t); } \ template inline T letoh(T t, SizeT) { return le ## bits ## toh(t); } BYTESWAPS(16) BYTESWAPS(32) BYTESWAPS(64) #undef BYTESWAPS template inline T htobe(T t) { return htobe(t, SizeT()); } template inline T htole(T t) { return htole(t, SizeT()); } template inline T betoh(T t) { return betoh(t, SizeT()); } template inline T letoh(T t) { return letoh(t, SizeT()); } int main() { std::cout << std::hex; std::cout << htobe(static_cast(0xfeca)) << '\n'; std::cout << htobe(0xafbeadde) << '\n'; // Use ULL suffix to specify integer constant as unsigned long long std::cout << htobe(0xfecaefbeafdeedfeULL) << '\n'; } 

Produzione:

 cafe deadbeaf feeddeafbeefcafe 

mi piace questo, solo per lo stile 🙂

 long swap(long i) { char *c = (char *) &i; return * (long *) (char[]) {c[3], c[2], c[1], c[0] }; } 

Seriamente … Non capisco perché tutte le soluzioni siano così complicate ! Che ne dici della funzione di modello più semplice e generica che scambia qualsiasi tipo di dimensione in qualsiasi circostanza in qualsiasi sistema operativo ????

 template  void SwapEnd(T& var) { char* varArray = reinterpret_cast(&var); for(long i = 0; i < static_cast(sizeof(var)/2); i++) std::swap(varArray[sizeof(var) - 1 - i],varArray[i]); } 

È il potere magico di C e C ++ insieme! Basta scambiare la variabile originale carattere per carattere.

Ricorda che non ho usato l’operatore di assegnazione semplice “=” perché alcuni oggetti saranno incasinati quando l’endianness viene capovolto e il costruttore di copia (o l’operatore di assegnazione) non funzionerà. Pertanto, è più affidabile copiarli in carattere char.

Per chiamarlo, basta usare

 double x = 5; SwapEnd(x); 

e ora x è diverso in endianness.

Ho questo codice che mi consente di convertire da HOST_ENDIAN_ORDER (qualunque esso sia) a LITTLE_ENDIAN_ORDER o BIG_ENDIAN_ORDER. Io uso un modello, quindi se provo a convertire da HOST_ENDIAN_ORDER a LITTLE_ENDIAN_ORDER e capita di essere lo stesso per la macchina per cui compilo, non verrà generato alcun codice.

Ecco il codice con alcuni commenti:

 // We define some constant for little, big and host endianess. Here I use // BOOST_LITTLE_ENDIAN/BOOST_BIG_ENDIAN to check the host indianess. If you // don't want to use boost you will have to modify this part a bit. enum EEndian { LITTLE_ENDIAN_ORDER, BIG_ENDIAN_ORDER, #if defined(BOOST_LITTLE_ENDIAN) HOST_ENDIAN_ORDER = LITTLE_ENDIAN_ORDER #elif defined(BOOST_BIG_ENDIAN) HOST_ENDIAN_ORDER = BIG_ENDIAN_ORDER #else #error "Impossible de determiner l'indianness du systeme cible." #endif }; // this function swap the bytes of values given it's size as a template // parameter (could sizeof be used?). template  inline T SwapBytes(T value) { union { T value; char bytes[size]; } in, out; in.value = value; for (unsigned int i = 0; i < size / 2; ++i) { out.bytes[i] = in.bytes[size - 1 - i]; out.bytes[size - 1 - i] = in.bytes[i]; } return out.value; } // Here is the function you will use. Again there is two compile-time assertion // that use the boost librarie. You could probably comment them out, but if you // do be cautious not to use this function for anything else than integers // types. This function need to be calles like this : // // int x = someValue; // int i = EndianSwapBytes(x); // template inline T EndianSwapBytes(T value) { // A : La donnée à swapper à une taille de 2, 4 ou 8 octets BOOST_STATIC_ASSERT(sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8); // A : La donnée à swapper est d'un type arithmetic BOOST_STATIC_ASSERT(boost::is_arithmetic::value); // Si from et to sont du même type on ne swap pas. if (from == to) return value; return SwapBytes(value); } 

Se un intero senza segno a 32 bit big-endian assomiglia a 0xAABBCCDD che è uguale a 2864434397, allora lo stesso intero senza segno a 32 bit assomiglia a 0xDDCCBBAA su un processore little-endian che è anche uguale a 2864434397.

Se un byte unsigned big-endian a 16 bit assomiglia a 0xAABB che è uguale a 43707, allora lo stesso short unsigned a 16 bit assomiglia a 0xBBAA su un processore little-endian che è anche uguale a 43707.

Ecco un paio di utili funzioni #define per scambiare byte da little-endian a big-endian e viceversa ->

 // can be used for short, unsigned short, word, unsigned word (2-byte types) #define BYTESWAP16(n) (((n&0xFF00)>>8)|((n&0x00FF)<<8)) // can be used for int or unsigned int or float (4-byte types) #define BYTESWAP32(n) ((BYTESWAP16((n&0xFFFF0000)>>16))|((BYTESWAP16(n&0x0000FFFF))<<16)) // can be used for unsigned long long or double (8-byte types) #define BYTESWAP64(n) ((BYTESWAP32((n&0xFFFFFFFF00000000)>>32))|((BYTESWAP32(n&0x00000000FFFFFFFF))<<32)) 

Ecco una versione generalizzata che mi è venuta in mente, per scambiare un valore sul posto. Gli altri suggerimenti sarebbero migliori se le prestazioni fossero un problema.

  template void ByteSwap(T * p) { for (int i = 0; i < sizeof(T)/2; ++i) std::swap(((char *)p)[i], ((char *)p)[sizeof(T)-1-i]); } 

Disclaimer: non ho ancora provato a compilarlo oa testarlo.

Se si prende il modello comune per invertire l’ordine dei bit in una parola e si seleziona la parte che inverte i bit all’interno di ciascun byte, si rimane con qualcosa che inverte solo i byte all’interno di una parola. Per 64 bit:

 x = ((x & 0x00000000ffffffff) << 32) ^ ((x >> 32) & 0x00000000ffffffff); x = ((x & 0x0000ffff0000ffff) << 16) ^ ((x >> 16) & 0x0000ffff0000ffff); x = ((x & 0x00ff00ff00ff00ff) << 8) ^ ((x >> 8) & 0x00ff00ff00ff00ff); 

Il compilatore dovrebbe eliminare le operazioni di mascheramento dei bit superflui (li ho lasciati per evidenziare il modello), ma se così non fosse, puoi riscrivere la prima riga in questo modo:

 x = ( x << 32) ^ (x >> 32); 

Questo dovrebbe normalmente semplificare fino a una singola istruzione di rotazione sulla maggior parte delle architetture (ignorando che l’intera operazione è probabilmente un’istruzione).

Su un processore RISC le costanti grandi e complicate possono causare difficoltà al compilatore. Tuttavia, puoi calcolare banalmente ognuna delle costanti dalla precedente. Così:

 uint64_t k = 0x00000000ffffffff; /* compiler should know a trick for this */ x = ((x & k) << 32) ^ ((x >> 32) & k); k ^= k << 16; x = ((x & k) << 16) ^ ((x >> 16) & k); k ^= k << 8; x = ((x & k) << 8) ^ ((x >> 8) & k); 

Se ti piace, puoi scriverlo come un loop. Non sarà efficiente, ma solo per divertimento:

 int i = sizeof(x) * CHAR_BIT / 2; uintmax_t k = (1 << i) - 1; while (i >= 8) { x = ((x & k) << i) ^ ((x >> i) & k); i >>= 1; k ^= k << i; } 

E per completezza, ecco la versione semplificata a 32 bit del primo modulo:

 x = ( x << 16) ^ (x >> 16); x = ((x & 0x00ff00ff) << 8) ^ ((x >> 8) & 0x00ff00ff); 

Ho pensato di aggiungere la mia soluzione qui da quando non l’ho visto da nessuna parte. È una piccola e portatile funzione basata su C ++ e portatile che utilizza solo operazioni bit.

 template inline static T swapByteOrder(const T& val) { int totalBytes = sizeof(val); T swapped = (T) 0; for (int i = 0; i < totalBytes; ++i) { swapped |= (val >> (8*(totalBytes-i-1)) & 0xFF) << (8*i); } return swapped; } 

Con i codici indicati qui sotto puoi scambiare facilmente tra BigEndian e LittleEndian

 #define uint32_t unsigned #define uint16_t unsigned short #define swap16(x) ((((uint16_t)(x) & 0x00ff)<<8)| \ (((uint16_t)(x) & 0xff00)>>8)) #define swap32(x) ((((uint32_t)(x) & 0x000000ff)<<24)| \ (((uint32_t)(x) & 0x0000ff00)<<8)| \ (((uint32_t)(x) & 0x00ff0000)>>8)| \ (((uint32_t)(x) & 0xff000000)>>24)) 

Sono davvero sorpreso che nessuno abbia menzionato le funzioni htobeXX e betohXX. Sono definiti in endian.h e sono molto simili alle funzioni di rete htonXX.

Wow, non potevo credere ad alcune delle risposte che ho letto qui. In realtà c’è un’istruzione in assembly che lo fa più velocemente di qualsiasi altra cosa. bswap. Potresti semplicemente scrivere una funzione come questa …

 __declspec(naked) uint32_t EndianSwap(uint32 value) { __asm { mov eax, dword ptr[esp + 4] bswap eax ret } } 

È MOLTO più veloce degli intrinseci che sono stati suggeriti. Li ho smontati e guardato. La funzione di cui sopra non ha alcun prologo / epilogo quindi praticamente non ha alcun sovraccarico.

 unsigned long _byteswap_ulong(unsigned long value); 

Fare 16 bit è altrettanto facile, con l’eccezione che useresti xchg al, ah. bswap funziona solo su registri a 32 bit.

64-bit è un po ‘più complicato, ma non eccessivamente. Molto meglio di tutti gli esempi precedenti con loop e template ecc.

Ci sono alcuni avvertimenti qui … Innanzitutto bswap è disponibile solo su CPU 80×486 e superiori. Qualcuno ha in programma di eseguirlo su un 386?!? Se è così, puoi comunque sostituire bswap con …

 mov ebx, eax shr ebx, 16 xchg bl, bh xchg al, ah shl eax, 16 or eax, ebx 

Anche l’assembly inline è disponibile solo nel codice x86 in Visual Studio. Una funzione nuda non può essere allineata e inoltre non è disponibile nelle versioni x64. In quell’istanza, dovrai usare le intrinseche del compilatore.

Tecnica portatile per l’implementazione di accessor non allineati non endian compatibili con l’ottimizzatore. Funzionano su ogni compilatore, ogni allineamento dei limiti e ogni ordine dei byte. Queste routine non allineate sono completate o aggiunte a seconda dell’endian nativo e dell’allineamento. Elenco parziale ma si ottiene l’idea. BO * sono valori costanti basati sull’ordinamento nativo di byte.

 uint32_t sw_get_uint32_1234(pu32) uint32_1234 *pu32; { union { uint32_1234 u32_1234; uint32_t u32; } bou32; bou32.u32_1234[0] = (*pu32)[BO32_0]; bou32.u32_1234[1] = (*pu32)[BO32_1]; bou32.u32_1234[2] = (*pu32)[BO32_2]; bou32.u32_1234[3] = (*pu32)[BO32_3]; return(bou32.u32); } void sw_set_uint32_1234(pu32, u32) uint32_1234 *pu32; uint32_t u32; { union { uint32_1234 u32_1234; uint32_t u32; } bou32; bou32.u32 = u32; (*pu32)[BO32_0] = bou32.u32_1234[0]; (*pu32)[BO32_1] = bou32.u32_1234[1]; (*pu32)[BO32_2] = bou32.u32_1234[2]; (*pu32)[BO32_3] = bou32.u32_1234[3]; } #if HAS_SW_INT64 int64 sw_get_int64_12345678(pi64) int64_12345678 *pi64; { union { int64_12345678 i64_12345678; int64 i64; } boi64; boi64.i64_12345678[0] = (*pi64)[BO64_0]; boi64.i64_12345678[1] = (*pi64)[BO64_1]; boi64.i64_12345678[2] = (*pi64)[BO64_2]; boi64.i64_12345678[3] = (*pi64)[BO64_3]; boi64.i64_12345678[4] = (*pi64)[BO64_4]; boi64.i64_12345678[5] = (*pi64)[BO64_5]; boi64.i64_12345678[6] = (*pi64)[BO64_6]; boi64.i64_12345678[7] = (*pi64)[BO64_7]; return(boi64.i64); } #endif int32_t sw_get_int32_3412(pi32) int32_3412 *pi32; { union { int32_3412 i32_3412; int32_t i32; } boi32; boi32.i32_3412[2] = (*pi32)[BO32_0]; boi32.i32_3412[3] = (*pi32)[BO32_1]; boi32.i32_3412[0] = (*pi32)[BO32_2]; boi32.i32_3412[1] = (*pi32)[BO32_3]; return(boi32.i32); } void sw_set_int32_3412(pi32, i32) int32_3412 *pi32; int32_t i32; { union { int32_3412 i32_3412; int32_t i32; } boi32; boi32.i32 = i32; (*pi32)[BO32_0] = boi32.i32_3412[2]; (*pi32)[BO32_1] = boi32.i32_3412[3]; (*pi32)[BO32_2] = boi32.i32_3412[0]; (*pi32)[BO32_3] = boi32.i32_3412[1]; } uint32_t sw_get_uint32_3412(pu32) uint32_3412 *pu32; { union { uint32_3412 u32_3412; uint32_t u32; } bou32; bou32.u32_3412[2] = (*pu32)[BO32_0]; bou32.u32_3412[3] = (*pu32)[BO32_1]; bou32.u32_3412[0] = (*pu32)[BO32_2]; bou32.u32_3412[1] = (*pu32)[BO32_3]; return(bou32.u32); } void sw_set_uint32_3412(pu32, u32) uint32_3412 *pu32; uint32_t u32; { union { uint32_3412 u32_3412; uint32_t u32; } bou32; bou32.u32 = u32; (*pu32)[BO32_0] = bou32.u32_3412[2]; (*pu32)[BO32_1] = bou32.u32_3412[3]; (*pu32)[BO32_2] = bou32.u32_3412[0]; (*pu32)[BO32_3] = bou32.u32_3412[1]; } float sw_get_float_1234(pf) float_1234 *pf; { union { float_1234 f_1234; float f; } bof; bof.f_1234[0] = (*pf)[BO32_0]; bof.f_1234[1] = (*pf)[BO32_1]; bof.f_1234[2] = (*pf)[BO32_2]; bof.f_1234[3] = (*pf)[BO32_3]; return(bof.f); } void sw_set_float_1234(pf, f) float_1234 *pf; float f; { union { float_1234 f_1234; float f; } bof; bof.f = (float)f; (*pf)[BO32_0] = bof.f_1234[0]; (*pf)[BO32_1] = bof.f_1234[1]; (*pf)[BO32_2] = bof.f_1234[2]; (*pf)[BO32_3] = bof.f_1234[3]; } double sw_get_double_12345678(pd) double_12345678 *pd; { union { double_12345678 d_12345678; double d; } bod; bod.d_12345678[0] = (*pd)[BO64_0]; bod.d_12345678[1] = (*pd)[BO64_1]; bod.d_12345678[2] = (*pd)[BO64_2]; bod.d_12345678[3] = (*pd)[BO64_3]; bod.d_12345678[4] = (*pd)[BO64_4]; bod.d_12345678[5] = (*pd)[BO64_5]; bod.d_12345678[6] = (*pd)[BO64_6]; bod.d_12345678[7] = (*pd)[BO64_7]; return(bod.d); } void sw_set_double_12345678(pd, d) double_12345678 *pd; double d; { union { double_12345678 d_12345678; double d; } bod; bod.d = d; (*pd)[BO64_0] = bod.d_12345678[0]; (*pd)[BO64_1] = bod.d_12345678[1]; (*pd)[BO64_2] = bod.d_12345678[2]; (*pd)[BO64_3] = bod.d_12345678[3]; (*pd)[BO64_4] = bod.d_12345678[4]; (*pd)[BO64_5] = bod.d_12345678[5]; (*pd)[BO64_6] = bod.d_12345678[6]; (*pd)[BO64_7] = bod.d_12345678[7]; } 

These typedefs have the benefit of raising compiler errors if not used with accessors, thus mitigating forgotten accessor bugs.

 typedef char int8_1[1], uint8_1[1]; typedef char int16_12[2], uint16_12[2]; /* little endian */ typedef char int16_21[2], uint16_21[2]; /* big endian */ typedef char int24_321[3], uint24_321[3]; /* Alpha Micro, PDP-11 */ typedef char int32_1234[4], uint32_1234[4]; /* little endian */ typedef char int32_3412[4], uint32_3412[4]; /* Alpha Micro, PDP-11 */ typedef char int32_4321[4], uint32_4321[4]; /* big endian */ typedef char int64_12345678[8], uint64_12345678[8]; /* little endian */ typedef char int64_34128756[8], uint64_34128756[8]; /* Alpha Micro, PDP-11 */ typedef char int64_87654321[8], uint64_87654321[8]; /* big endian */ typedef char float_1234[4]; /* little endian */ typedef char float_3412[4]; /* Alpha Micro, PDP-11 */ typedef char float_4321[4]; /* big endian */ typedef char double_12345678[8]; /* little endian */ typedef char double_78563412[8]; /* Alpha Micro? */ typedef char double_87654321[8]; /* big endian */ 

I recently wrote a macro to do this in C, but it’s equally valid in C++:

 #define REVERSE_BYTES(...) do for(size_t REVERSE_BYTES=0; REVERSE_BYTES>1; ++REVERSE_BYTES)\ ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES],\ ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES],\ ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES];\ while(0) 

It accepts any type and reverses the bytes in the passed argument. Example usages:

 int main(){ unsigned long long x = 0xABCDEF0123456789; printf("Before: %llX\n",x); REVERSE_BYTES(x); printf("After : %llX\n",x); char c[7]="nametag"; printf("Before: %c%c%c%c%c%c%c\n",c[0],c[1],c[2],c[3],c[4],c[5],c[6]); REVERSE_BYTES(c); printf("After : %c%c%c%c%c%c%c\n",c[0],c[1],c[2],c[3],c[4],c[5],c[6]); } 

Quale stampa:

 Before: ABCDEF0123456789 After : 8967452301EFCDAB Before: nametag After : gateman 

The above is perfectly copy/paste-able, but there’s a lot going on here, so I’ll break down how it works piece by piece:

The first notable thing is that the entire macro is encased in a do while(0) block. This is a common idiom to allow normal semicolon use after the macro.

Next up is the use of a variable named REVERSE_BYTES as the for loop’s counter. The name of the macro itself is used as a variable name to ensure that it doesn’t clash with any other symbols that may be in scope wherever the macro is used. Since the name is being used within the macro’s expansion, it won’t be expanded again when used as a variable name here.

Within the for loop, there are two bytes being referenced and XOR swapped (so a temporary variable name is not required):

 ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES] ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES] 

__VA_ARGS__ represents whatever was given to the macro, and is used to increase the flexibility of what may be passed in (albeit not by much). The address of this argument is then taken and cast to an unsigned char pointer to permit the swapping of its bytes via array [] subscripting.

The final peculiar point is the lack of {} braces. They aren’t necessary because all of the steps in each swap are joined with the comma operator , making them one statement.

Finally, it’s worth noting that this is not the ideal approach if speed is a top priority. If this is an important factor, some of the type-specific macros or platform-specific directives referenced in other answers are likely a better option. This approach, however, is portable to all types, all major platforms, and both the C and C++ languages.

Here’s how to read a double stored in IEEE 754 64 bit format, even if your host computer uses a different system.

 /* * read a double from a stream in ieee754 format regardless of host * encoding. * fp - the stream * bigendian - set to if big bytes first, clear for little bytes * first * */ double freadieee754(FILE *fp, int bigendian) { unsigned char buff[8]; int i; double fnorm = 0.0; unsigned char temp; int sign; int exponent; double bitval; int maski, mask; int expbits = 11; int significandbits = 52; int shift; double answer; /* read the data */ for (i = 0; i < 8; i++) buff[i] = fgetc(fp); /* just reverse if not big-endian*/ if (!bigendian) { for (i = 0; i < 4; i++) { temp = buff[i]; buff[i] = buff[8 - i - 1]; buff[8 - i - 1] = temp; } } sign = buff[0] & 0x80 ? -1 : 1; /* exponet in raw format*/ exponent = ((buff[0] & 0x7F) << 4) | ((buff[1] & 0xF0) >> 4); /* read inthe mantissa. Top bit is 0.5, the successive bits half*/ bitval = 0.5; maski = 1; mask = 0x08; for (i = 0; i < significandbits; i++) { if (buff[maski] & mask) fnorm += bitval; bitval /= 2.0; mask >>= 1; if (mask == 0) { mask = 0x80; maski++; } } /* handle zero specially */ if (exponent == 0 && fnorm == 0) return 0.0; shift = exponent - ((1 << (expbits - 1)) - 1); /* exponent = shift + bias */ /* nans have exp 1024 and non-zero mantissa */ if (shift == 1024 && fnorm != 0) return sqrt(-1.0); /*infinity*/ if (shift == 1024 && fnorm == 0) { #ifdef INFINITY return sign == 1 ? INFINITY : -INFINITY; #endif return (sign * 1.0) / 0.0; } if (shift > -1023) { answer = ldexp(fnorm + 1.0, shift); return answer * sign; } else { /* denormalised numbers */ if (fnorm == 0.0) return 0.0; shift = -1022; while (fnorm < 1.0) { fnorm *= 2; shift--; } answer = ldexp(fnorm, shift); return answer * sign; } } 

For the rest of the suite of functions, including the write and the integer routines see my github project

https://github.com/MalcolmMcLean/ieee754

Try Boost::endian , and DO NOT IMPLEMENT IT YOURSELF!

Here’s a link

Look up bit shifting, as this is basically all you need to do to swap from little -> big endian. Then depending on the bit size, you change how you do the bit shifting.