Comment convertir des valeurs big-endian et little-endian en C ++?

Comment convertir des valeurs big-endian et little-endian en C ++?

EDIT: Pour plus de clarté, je dois traduire des données binarys (valeurs à virgule flottante double précision et entiers 32 bits et 64 bits) d’une architecture CPU à une autre. Cela n’implique pas de réseautage, alors ntoh () et des fonctions similaires ne fonctionneront pas ici.

EDIT # 2: La réponse que j’ai acceptée s’applique directement aux compilateurs que je cible (ce qui explique pourquoi je l’ai choisi). Cependant, il existe d’autres très bonnes réponses, plus portables.

Si vous utilisez Visual C ++, procédez comme suit: Vous incluez insortingn.h et appelez les fonctions suivantes:

Pour les numéros de 16 bits:

 unsigned short _byteswap_ushort(unsigned short value); 

Pour les numéros à 32 bits:

 unsigned long _byteswap_ulong(unsigned long value); 

Pour les nombres de 64 bits:

 unsigned __int64 _byteswap_uint64(unsigned __int64 value); 

Les nombres de 8 bits (caractères) n’ont pas besoin d’être convertis.

Ils ne sont également définis que pour les valeurs non signées, ils fonctionnent également pour les entiers signés.

Pour les flottants et les doubles, il est plus difficile que pour les entiers simples car ceux-ci peuvent ou non se trouver dans l’ordre des octets des machines hôtes. Vous pouvez obtenir des flottants little-endian sur les machines big-endian et vice-versa.

D’autres compilateurs ont également des caractéristiques insortingnsèques similaires.

Dans GCC par exemple, vous pouvez appeler directement:

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

(pas besoin d’inclure quelque chose). Afaik bits.h déclare également la même fonction de manière non gcc.

Permuter 16 bits c’est juste un peu de rotation.

Le fait d’appeler les éléments insortingnsèques au lieu de lancer les vôtres vous donne les meilleures performances et la plus grande densité de code possibles.

Tout simplement:

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

usage: swap_endian(42) .

From The Byte Order Fallacy de Rob Pyke:

Disons que votre stream de données a un nombre entier de 32 bits codé par Endian. Voici comment l’extraire (en supposant des octets non signés):

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

Si c'est big-endian, voici comment l'extraire:

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

TL; DR: ne vous préoccupez pas de l'ordre natif de votre plate-forme, tout ce qui compte, c'est l'ordre des octets du stream que vous lisez, et vous espérez qu'il soit bien défini.

Note: il a été remarqué dans le commentaire qu’en l’absence de conversion de type explicite, il était important que les data soient un tableau de unsigned char ou de uint8_t . L'utilisation de caractères ou de caractères signed char (s'ils sont signés) entraînera la promotion des data[x] en un entier et le data[x] << 24 dans le bit de signe qui correspond à UB.

Si vous le faites à des fins de compatibilité réseau / hôte, vous devez utiliser:

 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) 

Si vous le faites pour une autre raison, l’une des solutions byte_swap présentées ici fonctionnerait correctement.

J’ai pris quelques suggestions de cet article et les ai rassemblées pour former ceci:

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

Il y a une instruction d’assemblage appelée BSWAP qui fera le swap pour vous, extrêmement rapide . Vous pouvez lire à ce sujet ici .

Visual Studio, ou plus précisément la bibliothèque d’exécution Visual C ++, possède des éléments insortingnsèques à la plate-forme, appelés _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64() . Des exemples similaires devraient exister pour d’autres plates-formes, mais je ne suis pas au courant de ce qu’ils seraient appelés.

La procédure pour passer du big endian au little endian est la même que pour passer du petit endian au big endian.

Voici un exemple de code:

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

Nous l’avons fait avec des modèles. Vous pourriez donc quelque chose comme ça:

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

Si vous faites cela pour transférer des données entre différentes plates-formes, examinez les fonctions ntoh et hton.

De la même façon que vous le faites en C:

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

Vous pouvez également déclarer un vecteur de caractères non signés, y mémoriser la valeur d’entrée, inverser les octets dans un autre vecteur et mémoriser les octets, mais cela prendra des ordres de grandeur plus longs que le bit-twidding, en particulier avec les valeurs 64 bits.

Sur la plupart des systèmes POSIX (via la norme POSIX), il y a endian.h, qui peut être utilisé pour déterminer le codage utilisé par votre système. De là, c’est comme ça:

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

Cela change l'ordre (du big endian au little endian):

Si vous avez le numéro 0xDEADBEEF (sur un petit système endian stocké sous la forme 0xEFBEADDE), ptr [0] sera 0xEF, ptr [1] est 0xBE, etc.

Mais si vous voulez l'utiliser pour la mise en réseau, alors htons, htonl et htonll (et leurs ntohs inverses, ntohl et ntohll) seront utiles pour convertir l'ordre des hôtes en ordre réseau.

Notez que, au moins pour Windows, htonl () est beaucoup plus lent que son homologue insortingnsèque _byteswap_ulong (). Le premier est un appel de bibliothèque DLL dans ws2_32.dll, le dernier est une instruction d’assemblage BSWAP. Par conséquent, si vous écrivez du code dépendant de la plate-forme, préférez utiliser les éléments insortingnsèques de la vitesse:

 #define htonl(x) _byteswap_ulong(x) 

Cela peut être particulièrement important pour le traitement d’image .PNG où tous les entiers sont enregistrés dans Big Endian avec une explication “On peut utiliser htonl () …” {pour ralentir les programmes Windows typiques, si vous n’êtes pas préparé}.

La plupart des plates-formes ont un fichier d’en-tête système qui fournit des fonctions byteswap efficaces. Sous Linux, il se trouve dans . Vous pouvez le faire bien en 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'; } 

Sortie:

 cafe deadbeaf feeddeafbeefcafe 

J’aime celui-ci, juste pour le style 🙂

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

Sérieusement … Je ne comprends pas pourquoi toutes les solutions sont si compliquées ! Que diriez-vous de la fonction de modèle la plus simple et la plus générale qui permette d’échanger n’importe quel type de n’importe quelle taille, quelles que soient les circonstances, dans n’importe quel système d’exploitation?

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

C’est le pouvoir magique de C et C ++ ensemble! Échangez simplement le caractère de variable d’origine par caractère.

Rappelez-vous que je n’ai pas utilisé l’opérateur d’affectation simple “=” car certains objects seront endommagés lorsque l’internet sera retourné et que le constructeur de la copie (ou l’opérateur d’affectation) ne fonctionnera pas. Par conséquent, il est plus fiable de les copier par char.

Pour l’appeler, utilisez simplement

 double x = 5; SwapEnd(x); 

et maintenant x est différent en endianness.

J’ai ce code qui me permet de convertir HOST_ENDIAN_ORDER (quel qu’il soit) en LITTLE_ENDIAN_ORDER ou BIG_ENDIAN_ORDER. J’utilise un modèle, donc si j’essaye de convertir de HOST_ENDIAN_ORDER à LITTLE_ENDIAN_ORDER et qu’ils se trouvent être les mêmes pour la machine pour laquelle je comstack, aucun code ne sera généré.

Voici le code avec quelques commentaires:

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

Si un entier non signé 32 bits big-endian ressemble à 0xAABBCCDD qui est égal à 2864434397, alors ce même entier non signé 32 bits ressemble à 0xDDCCBBAA sur un processeur little-endian qui est également égal à 2864434397.

Si un short non signé 16 bits big-endian ressemble à 0xAABB qui est égal à 43707, alors ce même court-circuit non signé 16 bits ressemble à 0xBBAA sur un processeur little-endian qui est également égal à 43707.

Voici quelques fonctions #define pratiques pour échanger des octets de little-endian vers big-endian et vice-versa ->

 // 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)) 

Voici une version généralisée que j’ai imaginée pour échanger une valeur en place. Les autres suggestions seraient mieux si la performance est un problème.

  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: Je n'ai pas encore essayé de le comstackr ou de le tester.

Si vous prenez le modèle commun pour inverser l’ordre des bits dans un mot et supprimez la partie qui inverse les bits dans chaque octet, il vous rest quelque chose qui n’inverse que les octets dans un mot. Pour 64 bits:

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

Le compilateur doit nettoyer les opérations de masquage superflues (je les ai laissées pour mettre en évidence le motif), mais si ce n’est pas le cas, vous pouvez réécrire la première ligne de cette façon:

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

Cela devrait normalement simplifier jusqu’à une seule instruction de rotation sur la plupart des architectures (en ignorant que l’opération entière est probablement une instruction).

Sur un processeur RISC, les grandes constantes complexes peuvent causer des difficultés au compilateur. Cependant, vous pouvez calculer sortingvialement chacune des constantes du précédent. Ainsi:

 uint64_t k = 0x00000000ffffffff; /* comstackr should know a sortingck 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); 

Si vous le souhaitez, vous pouvez écrire cela en boucle. Ce ne sera pas efficace, mais juste pour le plaisir:

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

Et pour être complet, voici la version simplifiée 32 bits du premier formulaire:

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

Je pensais juste avoir ajouté ma propre solution ici puisque je ne l’ai vue nulle part. C’est une petite fonction portable, basée sur des modèles C ++, et portable qui n’utilise que des opérations sur les bits.

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

Avec les codes donnés ci-dessous, vous pouvez échanger facilement BigEndian et 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)) 

Je suis vraiment surpris que personne n’ait mentionné les fonctions htobeXX et betohXX. Ils sont définis dans endian.h et sont très similaires aux fonctions réseau htonXX.

Wow, je ne pouvais pas croire certaines des réponses que j’ai lues ici. Il y a en fait une instruction d’assemblage qui le fait plus rapidement que toute autre chose. bswap. Vous pourriez simplement écrire une fonction comme celle-ci …

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

Il est BEAUCOUP plus rapide que les éléments insortingnsèques suggérés. Je les ai démontés et regardé. La fonction ci-dessus n’a pas de prolog / épilogue, elle n’a donc pratiquement aucune surcharge.

 unsigned long _byteswap_ulong(unsigned long value); 

Faire 16 bits est tout aussi simple, sauf que vous utiliseriez xchg al, ah. bswap ne fonctionne que sur les registres 32 bits.

64 bits est un peu plus compliqué, mais pas trop. Beaucoup mieux que tous les exemples ci-dessus avec des boucles et des modèles, etc.

Il y a quelques réserves ici … Premièrement, bswap est uniquement disponible sur les processeurs 80×486 et supérieurs. Est-ce que quelqu’un a l’intention de l’exécuter sur un 386? Si oui, vous pouvez toujours remplacer bswap par …

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

L’assemblage en ligne est également disponible uniquement dans le code x86 dans Visual Studio. Une fonction nue ne peut pas être alignée et n’est pas non plus disponible dans les versions x64. Dans ce cas, vous devrez utiliser les éléments insortingnsèques du compilateur.

Technique portable pour l’implémentation d’accesseurs endian non alignés, non alignés et optimisés pour l’optimiseur. Ils travaillent sur chaque compilateur, chaque alignement de limite et chaque ordre d’octet. Ces routines non alignées sont complétées ou envisagées en fonction de l’endian natif et de l’alignement. Liste partielle mais vous avez l’idée. BO * sont des valeurs constantes basées sur l’ordre des octets natifs.

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

Quelles impressions:

 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.