c++ - hexadecimal - little endian pdf
¿Cómo convierto entre los valores big-endian y little-endian en C++? (28)
¿Cómo convierto entre los valores big-endian y little-endian en C ++?
EDITAR: Para mayor claridad, tengo que traducir datos binarios (valores de coma flotante de precisión doble y enteros de 32 y 64 bits) de una arquitectura de CPU a otra. Esto no implica una red, por lo que ntoh () y funciones similares no funcionarán aquí.
EDIT # 2: La respuesta que acepté se aplica directamente a los compiladores a los que me dirijo (y por eso lo elegí). Sin embargo, hay otras respuestas muy buenas y más portátiles aquí.
Aquí hay una versión generalizada que surgió de la parte superior de mi cabeza, para intercambiar un valor en su lugar. Las otras sugerencias serían mejores si el rendimiento es un problema.
template<typename T>
void ByteSwap(T * p)
{
for (int i = 0; i < sizeof(T)/2; ++i)
std::swap(((char *)p)[i], ((char *)p)[sizeof(T)-1-i]);
}
Descargo de responsabilidad: no he tratado de compilar esto o probarlo todavía.
Con los códigos dados a continuación, puede cambiar fácilmente entre BigEndian y 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))
De la falacia del orden byte de Rob Pyke:
Digamos que su flujo de datos tiene un entero de 32 bits codificado en little-endian. Aquí se explica cómo extraerlo (suponiendo bytes sin firmar):
i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);
Si es big-endian, aquí está cómo extraerlo:
i = (data[3]<<0) | (data[2]<<8) | (data[1]<<16) | (data[0]<<24);
TL; DR: no se preocupe por el orden nativo de su plataforma, todo lo que cuenta es el orden de bytes de la transmisión de la que está leyendo, y es mejor que esté bien definido.
Nota: se comentó en el comentario que, en ausencia de conversión de tipo explícito, era importante que los data
fueran una matriz de caracteres unsigned char
o uint8_t
. El uso de signed char
o char
firmado (si está firmado) dará como resultado que los data[x]
sean promovidos a un entero y los data[x] << 24
potencialmente cambien un 1 al bit de signo que es UB.
De la misma manera que lo hace en C:
short big = 0xdead;
short little = (((big & 0xff)<<8) | ((big & 0xff00)>>8));
También podría declarar un vector de caracteres sin signo, insertar el valor de entrada en él, invertir los bytes en otro vector y eliminar los bytes, pero eso tomará órdenes de magnitud más largos que los movimientos de bit, especialmente con valores de 64 bits.
El procedimiento para ir de big-endian a little-endian es lo mismo que pasar de little-endian a big-endian.
Aquí hay un código de ejemplo:
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);
}
En la mayoría de los sistemas POSIX (no está en el estándar POSIX) está el endian.h, que se puede usar para determinar qué codificación usa su sistema. A partir de ahí, es algo como esto:
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];
}
Esto cambia el orden (de big-endian a little endian):
Si tiene el número 0xDEADBEEF (en un pequeño sistema endian almacenado como 0xEFBEADDE), ptr [0] será 0xEF, ptr [1] es 0xBE, etc.
Pero si desea usarlo para la creación de redes, entonces htons, htonl y htonll (y sus ntohs inversos, ntohl y ntohll) serán útiles para convertir de orden de host a orden de red.
En serio ... ¡No entiendo por qué todas las soluciones son tan complicadas ! ¿Qué hay de la función de plantilla más simple y general que intercambia cualquier tipo de cualquier tamaño bajo cualquier circunstancia en cualquier sistema operativo ???
template <typename T>
void SwapEnd(T& var)
{
char* varArray = reinterpret_cast<char*>(&var);
for(long i = 0; i < static_cast<long>(sizeof(var)/2); i++)
std::swap(varArray[sizeof(var) - 1 - i],varArray[i]);
}
¡Es el poder mágico de C y C ++ juntos! Simplemente cambie la variable original carácter por carácter.
Recuerde que no utilicé el operador de asignación simple "=" porque algunos objetos se arruinarán cuando se gira la endianidad y el constructor de copias (u operador de asignación) no funcionará. Por lo tanto, es más confiable copiarlos char por char.
Para llamarlo, solo use
double x = 5;
SwapEnd(x);
y ahora x
es diferente en endianness.
Estoy realmente sorprendido de que nadie haya mencionado las funciones de htobeXX y betohXX. Están definidos en endian.h y son muy similares a las funciones de red htonXX.
Hay una instrucción de ensamble llamada BSWAP que hará el intercambio por usted, extremadamente rápido . Puedes leer sobre esto here .
Visual Studio, o más precisamente la biblioteca de tiempo de ejecución de Visual C ++, tiene intrínsecos de plataforma para esto, llamados _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64()
. Similar debería existir para otras plataformas, pero no estoy al tanto de cómo se llamarían.
Hemos hecho esto con plantillas. Podrías algo como esto:
// 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);
}
La mayoría de las plataformas tienen un archivo de cabecera del sistema que proporciona funciones eficientes de byteswap. En Linux está en <endian.h>
. Puedes envolverlo muy bien en C ++:
#include <iostream>
#include <endian.h>
template<size_t N> struct SizeT {};
#define BYTESWAPS(bits) /
template<class T> inline T htobe(T t, SizeT<bits / 8>) { return htobe ## bits(t); } /
template<class T> inline T htole(T t, SizeT<bits / 8>) { return htole ## bits(t); } /
template<class T> inline T betoh(T t, SizeT<bits / 8>) { return be ## bits ## toh(t); } /
template<class T> inline T letoh(T t, SizeT<bits / 8>) { return le ## bits ## toh(t); }
BYTESWAPS(16)
BYTESWAPS(32)
BYTESWAPS(64)
#undef BYTESWAPS
template<class T> inline T htobe(T t) { return htobe(t, SizeT<sizeof t>()); }
template<class T> inline T htole(T t) { return htole(t, SizeT<sizeof t>()); }
template<class T> inline T betoh(T t) { return betoh(t, SizeT<sizeof t>()); }
template<class T> inline T letoh(T t) { return letoh(t, SizeT<sizeof t>()); }
int main()
{
std::cout << std::hex;
std::cout << htobe(static_cast<unsigned short>(0xfeca)) << ''/n'';
std::cout << htobe(0xafbeadde) << ''/n'';
// Use ULL suffix to specify integer constant as unsigned long long
std::cout << htobe(0xfecaefbeafdeedfeULL) << ''/n'';
}
Salida:
cafe
deadbeaf
feeddeafbeefcafe
Si está haciendo esto para fines de compatibilidad de red / host, debe usar:
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 está haciendo esto por alguna otra razón, una de las soluciones byte_swap que se presentan aquí funcionaría bien.
Si está haciendo esto para transferir datos entre diferentes plataformas, mire las funciones ntoh y hton.
Si toma el patrón común para invertir el orden de los bits en una palabra y elimina la parte que revierte los bits dentro de cada byte, entonces quedará algo que solo revierte los bytes dentro de una palabra. Para 64 bits:
x = ((x & 0x00000000ffffffff) << 32) ^ ((x >> 32) & 0x00000000ffffffff);
x = ((x & 0x0000ffff0000ffff) << 16) ^ ((x >> 16) & 0x0000ffff0000ffff);
x = ((x & 0x00ff00ff00ff00ff) << 8) ^ ((x >> 8) & 0x00ff00ff00ff00ff);
El compilador debería limpiar las operaciones superfluas de enmascaramiento de bits (las dejé para resaltar el patrón), pero si no puede reescribir la primera línea de esta manera:
x = ( x << 32) ^ (x >> 32);
Eso normalmente debería simplificar hasta una sola instrucción de rotación en la mayoría de las arquitecturas (ignorando que toda la operación probablemente sea una instrucción).
En un procesador RISC, las constantes grandes y complicadas pueden causar dificultades al compilador. Sin embargo, puedes calcular trivialmente cada una de las constantes del anterior. Al igual que:
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);
Si lo desea, puede escribir eso como un bucle. No será eficiente, solo por diversión:
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;
}
Y para completar, aquí está la versión simplificada de 32 bits de la primera forma:
x = ( x << 16) ^ (x >> 16);
x = ((x & 0x00ff00ff) << 8) ^ ((x >> 8) & 0x00ff00ff);
Si un entero sin signo de 32 bits big-endian se parece a 0xAABBCCDD que es igual a 2864434397, entonces ese mismo entero sin signo de 32 bits se parece a 0xDDCCBBAA en un procesador little-endian que también es igual a 2864434397.
Si un short sin firmar big-endian de 16 bits se ve como 0xAABB que es igual a 43707, entonces ese mismo short sin firmar de 16 bits se ve como 0xBBAA en un procesador little-endian que también es igual a 43707.
Aquí hay un par de funciones #define útiles para intercambiar bytes de little-endian a big-endian y 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))
Si usa Visual C ++, haga lo siguiente: Incluye intrin.h y llama a las siguientes funciones:
Para números de 16 bits:
unsigned short _byteswap_ushort(unsigned short value);
Para números de 32 bits:
unsigned long _byteswap_ulong(unsigned long value);
Para números de 64 bits:
unsigned __int64 _byteswap_uint64(unsigned __int64 value);
Los números de 8 bits (caracteres) no necesitan convertirse.
Además, estos solo están definidos para valores sin firmar, también funcionan para enteros con signo.
Para flotantes y dobles es más difícil que con enteros simples, ya que pueden estar o no en el orden de bytes de las máquinas anfitrionas. Puedes obtener flotadores little-endian en máquinas big-endian y viceversa.
Otros compiladores también tienen intrínsecos similares.
En GCC, por ejemplo, puede llamar directamente a:
int32_t __builtin_bswap32 (int32_t x)
int64_t __builtin_bswap64 (int64_t x)
(no es necesario incluir algo). Afaik bits.h declara la misma función de una manera no gcc-céntrica también.
El intercambio de 16 bits es solo un poco rotado.
Llamar a los intrínsecos en lugar de hacer los suyos le da el mejor rendimiento y la mejor densidad de códigos.
Simplemente pon:
#include <climits>
template <typename T>
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;
}
uso: swap_endian<uint32_t>(42)
.
Solo pensé que agregué mi propia solución aquí ya que no la he visto en ningún lado. Es una función pequeña y portátil con plantillas C ++ y portátil que solo usa operaciones de bits.
template<typename T> 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;
}
Tenga en cuenta que, al menos para Windows, htonl () es mucho más lento que su contraparte intrínseca _byteswap_ulong (). La primera es una llamada a la biblioteca DLL en ws2_32.dll, la última es una instrucción de ensamblaje BSWAP. Por lo tanto, si está escribiendo algún código dependiente de la plataforma, prefiera usar los intrínsecos para la velocidad:
#define htonl(x) _byteswap_ulong(x)
Esto puede ser especialmente importante para el procesamiento de imágenes .PNG donde todos los enteros se guardan en Big Endian con la explicación "Uno puede usar htonl () ..." {para ralentizar los programas típicos de Windows, si no está preparado}.
Tengo este código que me permite convertir de HOST_ENDIAN_ORDER (lo que sea) a LITTLE_ENDIAN_ORDER o BIG_ENDIAN_ORDER. Utilizo una plantilla, por lo que si intento convertir de HOST_ENDIAN_ORDER a LITTLE_ENDIAN_ORDER y resultan ser las mismas para la máquina para la que compilo, no se generará ningún código.
Aquí está el código con algunos comentarios:
// 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 <class T, unsigned int size>
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<HOST_ENDIAN_ORDER, BIG_ENDIAN_ORDER>(x);
//
template<EEndian from, EEndian to, class T>
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<T>::value);
// Si from et to sont du même type on ne swap pas.
if (from == to)
return value;
return SwapBytes<T, sizeof(T)>(value);
}
Tomé algunas sugerencias de esta publicación y las puse juntas para formar esto:
#include <boost/type_traits.hpp>
#include <boost/static_assert.hpp>
#include <boost/detail/endian.hpp>
#include <stdexcept>
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<typename T, size_t sz>
struct swap_bytes
{
inline T operator()(T val)
{
throw std::out_of_range("data size");
}
};
template<typename T>
struct swap_bytes<T, 1>
{
inline T operator()(T val)
{
return val;
}
};
template<typename T>
struct swap_bytes<T, 2>
{
inline T operator()(T val)
{
return ((((val) >> 8) & 0xff) | (((val) & 0xff) << 8));
}
};
template<typename T>
struct swap_bytes<T, 4>
{
inline T operator()(T val)
{
return ((((val) & 0xff000000) >> 24) |
(((val) & 0x00ff0000) >> 8) |
(((val) & 0x0000ff00) << 8) |
(((val) & 0x000000ff) << 24));
}
};
template<>
struct swap_bytes<float, 4>
{
inline float operator()(float val)
{
uint32_t mem =swap_bytes<uint32_t, sizeof(uint32_t)>()(*(uint32_t*)&val);
return *(float*)&mem;
}
};
template<typename T>
struct swap_bytes<T, 8>
{
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<double, 8>
{
inline double operator()(double val)
{
uint64_t mem =swap_bytes<uint64_t, sizeof(uint64_t)>()(*(uint64_t*)&val);
return *(double*)&mem;
}
};
template<endianness from, endianness to, class T>
struct do_byte_swap
{
inline T operator()(T value)
{
return swap_bytes<T, sizeof(T)>()(value);
}
};
// specialisations when attempting to swap to the same endianess
template<class T> struct do_byte_swap<little_endian, little_endian, T> { inline T operator()(T value) { return value; } };
template<class T> struct do_byte_swap<big_endian, big_endian, T> { inline T operator()(T value) { return value; } };
} // namespace detail
template<endianness from, endianness to, class T>
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<T>::value);
return detail::do_byte_swap<from, to, T>()(value);
}
Wow, no podía creer algunas de las respuestas que he leído aquí. De hecho, hay una instrucción en ensamblaje que lo hace más rápido que cualquier otra cosa. bswap. Simplemente podría escribir una función como esta ...
__declspec(naked) uint32_t EndianSwap(uint32 value)
{
__asm
{
mov eax, dword ptr[esp + 4]
bswap eax
ret
}
}
Es MUCHO más rápido que los intrínsecos que se han sugerido. Los he desarmado y miré. La función anterior no tiene prólogo / epílogo, por lo que prácticamente no tiene sobrecarga alguna.
unsigned long _byteswap_ulong(unsigned long value);
Hacer 16 bits es igual de fácil, con la excepción de que usarías xchg al, ah. bswap solo funciona en registros de 32 bits.
64 bits es un poco más complicado, pero no excesivamente. Mucho mejor que todos los ejemplos anteriores con bucles y plantillas, etc.
Aquí hay algunas advertencias ... En primer lugar, bswap solo está disponible para CPU 80x486 y superiores. ¿Alguien está planeando ejecutarlo en un 386?!? Si es así, aún puedes reemplazar bswap con ...
mov ebx, eax
shr ebx, 16
xchg bl, bh
xchg al, ah
shl eax, 16
or eax, ebx
También el ensamblaje en línea solo está disponible en código x86 en Visual Studio. Una función desnuda no se puede alinear y tampoco está disponible en versiones x64. En esa instancia, vas a tener que usar los intrínsecos del compilador.
me gusta este, solo por el estilo :-)
long swap(long i) {
char *c = (char *) &i;
return * (long *) (char[]) {c[3], c[2], c[1], c[0] };
}
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;
}
}
Para el resto del conjunto de funciones, incluidas las rutinas de escritura y entero, vea mi proyecto github
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<sizeof(__VA_ARGS__)>>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]);
}
Which prints:
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.
Portable technique for implementing optimizer-friendly unaligned non-inplace endian accessors. They work on every compiler, every boundary alignment and every byte ordering. These unaligned routines are supplemented, or mooted, depending on native endian and alignment. Partial listing but you get the idea. BO* are constant values based on native byte ordering.
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 */
¡Inténtalo Boost::endian
y NO LO IMPLEMENTES!
Aquí hay un link
Busque un poco de cambio, ya que esto es básicamente todo lo que necesita hacer para intercambiar desde pequeño -> big endian. Luego, dependiendo del tamaño del bit, cambia la forma en que realiza el cambio de bit.