¿Cómo convierto entre valores big-endian y little-endian en C ++?

196

¿Cómo convierto entre valores big-endian y little-endian en C ++?

EDITAR: para mayor claridad, tengo que traducir datos binarios (valores de coma flotante de doble precisión y enteros de 32 bits y 64 bits) de una arquitectura de CPU a otra. Esto no implica redes, por lo que ntoh () y funciones similares no funcionarán aquí.

EDITAR # 2: La respuesta que acepté se aplica directamente a los compiladores a los que me dirijo (por eso lo elegí). Sin embargo, hay otras respuestas muy buenas y más portátiles aquí.

Uhall
fuente
21
ntoh hton funcionará bien, incluso si no tiene nada que ver con las redes.
Ben Collins
2
La mejor manera de lidiar con la endianness en general es asegurarse de que el código se ejecute en máquinas host little-and-big-endian. Si eso funciona, probablemente lo hiciste bien. Asumir que estás en x86 / be es peligroso como práctica.
jakobengblom2
10
hton ntoh no funcionará si la máquina es big-endian, porque el que hace la pregunta explícitamente quiere realizar la conversión.
fabspro
66
@ jakobengblom2 es la única persona que menciona esto. Casi todos los ejemplos en esta página usan conceptos como bytes de "intercambio" en lugar de hacerlo agnóstico del endianness subyacente. Si se trata de formatos de archivo externos (que tienen una capacidad de definición bien definida), lo más portátil es tratar los datos externos como una secuencia de bytes y convertir la secuencia de bytes hacia y desde los enteros nativos. Me estremezco cada vez que veo short swap(short x)código, ya que se romperá si te mueves a una plataforma con diferente endianness. Matthieu M tiene la única respuesta correcta a continuación.
Mark Lakata
3
Estás pensando en el problema completamente equivocado. La tarea no es "cómo convierto entre valores big-endian y little-endian". La tarea es "cómo convierto los valores de punto flotante y entero en un formato particular al formato nativo de mi plataforma". Si lo hace bien, el formato nativo puede ser Big Endian, Little Endian, Mixed Endian o Ternary para todos sus cuidados de código.
David Schwartz

Respuestas:

166

Si está utilizando 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 ser convertidos.

Además, estos solo se definen para valores sin signo, también funcionan para enteros con signo.

Para flotantes y dobles es más difícil, ya que con enteros simples, ya que pueden o no estar en el orden de bytes de las máquinas host. Puede obtener carrozas little-endian en máquinas big-endian y viceversa.

Otros compiladores tienen intrínsecos similares también.

En GCC, por ejemplo, puede llamar directamente a algunos componentes internos como se documenta aquí :

uint32_t __builtin_bswap32 (uint32_t x)
uint64_t __builtin_bswap64 (uint64_t x)

(No es necesario incluir algo). Afaik bits.h declara la misma función de una manera no centrada en gcc también.

El intercambio de 16 bits es solo una rotación de bits.

Llamar a los intrínsecos en lugar de rodar el suyo le brinda el mejor rendimiento y densidad de código por cierto

Nils Pipenbrinck
fuente
11
Con GCC, podría usar: #include <byteswap.h> int32_t bswap_32 (int32_t x) int64_t bswap_64 (int64_t x)
jmanning2k
55
__builtin_bswapXsolo está disponible desde GCC-4.3 en adelante
Matt Joiner
20
También vale la pena señalar que estas funciones intrínsecas / siempre / Invertir bytes, no como son htonl, htons, etc Usted tiene que saber desde el contexto de su situación cuando en realidad intercambiar los bytes.
Brian Vandenberg
8
@ Jason porque los números de 8 bits son iguales en endian grande y pequeño. :-)
Nils Pipenbrinck
2
@BrianVandenberg Derecha; usar htonly ntohlsin preocuparse por el contexto funcionaría al escribir código portátil ya que la plataforma que define estas funciones lo intercambiaría si es little / mid-endian y en big-endian sería un no-op. Sin embargo, cuando se decodifica un tipo de archivo estándar que se define como little-endian (digamos BMP), uno todavía tiene que conocer el contexto y no puede confiar solo en htonly ntohl.
legends2k
86

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

Alexandre C.
fuente
3
Tener un voto a favor. Acabo de usar uchars, y asigné 4 a 1, 3 a 2, 2 a 3 y 1 a 4, pero esto es más flexible si tienes diferentes tamaños. 6 relojes en un 1er Gen Pentium IIRC. BSWAP es 1 reloj, pero es específico de la plataforma.
2
@RocketRoy: Sí, y si la velocidad resulta ser un problema, es muy sencillo escribir sobrecargas con intrínsecos específicos de plataforma y tipo.
Alexandre C.
3
@MihaiTodor: este estándar permite explícitamente el uso de uniones para la conversión de texto a través de una serie de caracteres. Ver por ej. esta pregunta .
Alexandre C.
44
@AlexandreC. No en el estándar C ++, solo en C. En C ++ (que es este código) este código es un comportamiento indefinido.
Rapptz
44
@Rapptz: 3.10 parece claro: "Si un programa intenta acceder al valor almacenado de un objeto a través de un valor gl diferente de uno de los siguientes tipos, el comportamiento es indefinido: [...] un tipo de carácter char o unsigned ". Tal vez me falta algo aquí, pero estaba bastante claro para mí que estaba explícitamente permitido acceder a cualquier tipo a través de punteros de caracteres.
Alexandre C.
75

De The Byte Order Fallacy de Rob Pike:

Digamos que su flujo de datos tiene un entero de 32 bits con codificación 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, lo único 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 observó en el comentario que, a falta de una conversión de tipo explícita, era importante que datafuera una matriz de unsigned charo uint8_t. Usar signed charo char(si está firmado) resultará en data[x]ser promovido a un número entero y data[x] << 24potencialmente cambiar un 1 al bit de signo que es UB.

Matthieu M.
fuente
66
Esto es genial, pero me parece que solo se aplica a los enteros y las variantes. ¿Qué hacer con flotadores / dobles?
Brett
1
@ v.oddou: sí y no, los archivos mapeados en memoria son exactamente iguales a los marcos de red; si acepta no leerlos directamente, lo único que importa es su endianness: si little-endian, use la primera fórmula, si es big-endian, use la segunda. Cualquier compilador que valga la pena optimizará las transformaciones innecesarias si el endianness coincide.
Matthieu M.
2
@meowsqueak: Sí, esperaría que funcione, porque solo cambia el orden de los bytes, no el orden de los bits dentro de cada byte.
Matthieu M.
3
En una nota poco relacionada, la publicación vinculada es una lectura desagradable ... El tipo parece valorar la brevedad, sin embargo, prefirió escribir una larga perorata sobre todos esos malos programadores que no están tan iluminados como él con respecto a la endianidad, en lugar de realmente explicando la situación y POR QUÉ su solución siempre funciona.
Anuncio N
1
Si está utilizando este método, asegúrese de enviar sus datos a (unsigned char *)
joseph
51

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 presentadas aquí funcionaría bien.

Escarchado
fuente
2
El orden de bytes de red es un gran endian, creo. Estas funciones se pueden usar con eso en mente, incluso si no está utilizando el código de red. Sin embargo, no hay versiones flotantes ntohf o htonf
Matt
2
Matt H. eso es casi siempre correcto. No todos los sistemas informáticos tienen un orden de bytes little endian. Si estaba trabajando, por ejemplo, en un motorolla 68k, un PowerPC u otra arquitectura big endian, estas funciones no intercambiarán bytes porque ya están en 'Orden de bytes de red'.
Frosty
2
Desafortunadamente, htonly ntohlno puedo ir a little endian en una plataforma big-endian.
Brian Vandenberg
2
@celtschk, entendido; sin embargo, el OP quiere una forma de cambiar la endianidad, incluso en un entorno big endian.
Brian Vandenberg
44
Para evitar la inevitable pregunta: hay una serie de razones para necesitar LE para una plataforma BE; varios formatos de archivo (bmp, fli, pcx, qtm, rtf, tga, por nombrar algunos) usan pequeños valores endian ... o al menos, alguna versión del formato lo hizo de una vez.
Brian Vandenberg
26

Tomé algunas sugerencias de esta publicación y las reuní 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);
}
Steve Lorimer
fuente
también debe incluir <cstdint> o <stdint.h>, por ejemplo, para uint32_t
ady
17

El procedimiento para pasar de big-endian a little-endian es el 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);
}
Kevin
fuente
2
La última función publicada aquí es incorrecta y debe editarse en: void swapByteOrder (unsigned long long & ull) {ull = (ull >> 56) | ... (ull << 56); }
Eric Burnett
14
No creo que sea correcto usar lógico y (&&) en lugar de bit a bit y (&). Según la especificación de C ++, ambos operandos se convierten implícitamente en bool, que no es lo que desea.
Trevor Robinson
16

Hay una instrucción de ensamblaje llamada BSWAP que hará el intercambio por usted, extremadamente rápido . Puedes leer sobre esto aquí .

Visual Studio, o más precisamente la biblioteca de tiempo de ejecución de Visual C ++, tiene una plataforma intrínseca para esto, llamada _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.

anon6439
fuente
Ese es un gran enlace. Se reavivó mi interés en el ensamblador x86.
PP.
1
Los resultados de tiempo para BSWAP se presentan aquí. gmplib.org/~tege/x86-timing.pdf ... y aquí ... agner.org/optimize/instruction_tables.pdf
12

Lo hemos hecho con plantillas. Podrías hacer 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);
}
marca
fuente
8

Si está haciendo esto para transferir datos entre diferentes plataformas, mire las funciones ntoh y hton.

Andrés
fuente
7

De la misma manera que lo haces en C:

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

También puede declarar un vector de caracteres sin signo, guardar 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 el giro de bits, especialmente con valores de 64 bits.

Ben Straub
fuente
7

En la mayoría de los sistemas POSIX (a través de que no está en el estándar POSIX) existe endian.h, que se puede utilizar para determinar qué codificación utiliza su sistema. A partir de ahí es algo como esto:

unsigned int change_endian(unsigned 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, htons, htonl y htonll (y sus inversas ntohs, ntohl y ntohll) serán útiles para convertir de orden de host a orden de red.

término
fuente
66
Eso es divertido: el estándar POSIX en opengroup.org/onlinepubs/9699919799/toc.htm no menciona un encabezado '<endian.h> `.
Jonathan Leffler
1
Puede usar htonly amigos independientemente de si el caso de uso tiene algo que ver con las redes. El orden de los bytes de la red es big-endian, así que solo trate esas funciones como host_to_be y be_to_host. (Sin embargo, no ayuda si necesita host_to_le.)
Peter Cordes
5

Tenga en cuenta que, al menos para Windows, htonl () es mucho más lento que su contraparte intrínseca _byteswap_ulong (). El primero es una llamada a la biblioteca DLL en ws2_32.dll, el último es una instrucción de ensamblaje BSWAP. Por lo tanto, si está escribiendo un 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}.

usuario2699548
fuente
4

La mayoría de las plataformas tienen un archivo de encabezado del sistema que proporciona funciones eficientes de intercambio de bytes. En Linux está adentro <endian.h>. Puedes envolverlo 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
Maxim Egorushkin
fuente
Cambio: #define BYTESWAPS (bits) \ template <clase T> en línea T htobe (T t, SizeT <bits / 8>) {return htobe ## bits (t); } \ template <clase T> en línea T htole (T t, SizeT <bits / 8>) {return htole ## bits (t); } \ template <clase T> en línea T betoh (T t, SizeT <bits / 8>) {return be ## bits ## toh (t); } \ template <clase T> en línea T letoh (T t, SizeT <bits / 8>) {return le ## bits ## toh (t); }
ldav1s
Gracias, olvidé probar betoh () y letoh ().
Maxim Egorushkin
4

me gusta este, solo por estilo :-)

long swap(long i) {
    char *c = (char *) &i;
    return * (long *) (char[]) {c[3], c[2], c[1], c[0] };
}
Friedemann
fuente
Recibo un error al char[]decir 'Error: no se permite el tipo incompleto'
Portland Runner
4

En serio ... ¡No entiendo por qué todas las soluciones son tan complicadas ! ¿Qué tal 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)
{
    static_assert(std::is_pod<T>::value, "Type must be POD type for safety");
    std::array<char, sizeof(T)> varArray;
    std::memcpy(varArray.data(), &var, sizeof(T));
    for(int i = 0; i < static_cast<int>(sizeof(var)/2); i++)
        std::swap(varArray[sizeof(var) - 1 - i],varArray[i]);
    std::memcpy(&var, varArray.data(), sizeof(T));
}

¡Es el poder mágico de C y C ++ juntos! Simplemente cambie la variable original carácter por carácter.

Punto 1 : Sin operadores: recuerde que no utilicé el operador de asignación simple "=" porque algunos objetos se estropearán cuando se invierta el endianness y el constructor de copia (u operador de asignación) no funcionará. Por lo tanto, es más confiable copiarlos char por char.

Punto 2 : Tenga en cuenta los problemas de alineación: tenga en cuenta que estamos copiando hacia y desde una matriz, que es lo correcto porque el compilador de C ++ no garantiza que podamos acceder a la memoria no alineada (esta respuesta se actualizó desde su original forma para esto). Por ejemplo, si asigna uint64_t, su compilador no puede garantizar que pueda acceder al 3er byte de eso como a uint8_t. Por lo tanto, lo correcto es copiar esto en una matriz de caracteres, intercambiarlo y luego copiarlo de nuevo (así que no reinterpret_cast). Tenga en cuenta que los compiladores son lo suficientemente inteligentes como para convertir lo que hizo de nuevo a reinterpret_castsi son capaces de acceder a bytes individuales independientemente de la alineación.

Para usar esta función :

double x = 5;
SwapEnd(x);

y ahora xes diferente en endianness.

El físico cuántico
fuente
2
Esto funcionará en cualquier lugar, pero el ocde de ensamblaje producido a menudo será subóptimo: vea mi pregunta stackoverflow.com/questions/36657895/…
j_kubik
Utiliza new/ deletepara asignar un búfer para esto?!? sizeof(var)es una constante de tiempo de compilación, por lo que podría hacerlo char varSwapped[sizeof(var)]. O podrías hacer char *p = reinterpret_cast<char*>(&var)e intercambiar en el lugar.
Peter Cordes
@ Peter esta respuesta es rápida y sucia para probar un punto. Implementaré tus sugerencias. Sin embargo, no tiene que ser un mega SO AH y votar por la solución de 5 líneas en comparación con las soluciones de 50 líneas que se ofrecen allí. No voy a decir más
The Quantum Physicist
Esta respuesta hace algunos puntos útiles sobre tener cuidado con los constructores y los operadores sobrecargados en datos endian incorrectos, por lo que me encantaría eliminar mi voto negativo una vez que el código no sea horrible, y es algo que un buen compilador podría compilar en un bswap instrucción. Además, sugeriría usar en for(size_t i = 0 ; i < sizeof(var) ; i++)lugar de a static_cast<long>. (O, en realidad, el intercambio en el lugar usará un ascendente y descendente char*para que desaparezca de todos modos).
Peter Cordes
por ejemplo, vea la respuesta de Mark Ransom usando std :: swap para invertir en el lugar.
Peter Cordes
3

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);
}
Mathieu Pagé
fuente
3

Si un entero sin signo big-endian de 32 bits 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 corto sin signo big-endian de 16 bits se parece a 0xAABB que es igual a 43707, entonces ese mismo corto sin signo de 16 bits se parece a 0xBBAA en un procesador little-endian que también es igual a 43707.

Aquí hay un par de prácticas funciones #define 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))
Adam Freeman
fuente
2

Aquí hay una versión generalizada que se me ocurrió, 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 intentado compilar esto o probarlo todavía.

Mark Ransom
fuente
2

Si toma el patrón común para invertir el orden de los bits en una palabra, y elimina la parte que invierte los bits dentro de cada byte, entonces queda algo que solo invierte 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 debe limpiar las operaciones de enmascaramiento de bits superfluas (las dejé para resaltar el patrón), pero si no lo hace, puede volver a escribir la primera línea de esta manera:

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

Eso normalmente debería simplificarse a una sola instrucción de rotación en la mayoría de las arquitecturas (ignorando que toda la operación es probablemente una instrucción).

En un procesador RISC, las constantes grandes y complicadas pueden causar dificultades en el compilador. Sin embargo, puede calcular trivialmente cada una de las constantes de la 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, pero 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);
sh1
fuente
2

Solo pensé que agregué mi propia solución aquí ya que no la había visto en ningún lado. Es una función con plantilla C ++ pequeña y portátil y portátil que solo utiliza 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;
}
Joao
fuente
2

Estoy realmente sorprendido de que nadie haya mencionado las funciones htobeXX y betohXX. Se definen en endian.h y son muy similares a las funciones de red htonXX.

Mella
fuente
2

Usando los códigos a continuación, puede intercambiar entre BigEndian y LittleEndian fácilmente

#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))
pz64_
fuente
1

Recientemente escribí una macro para hacer esto en C, pero es igualmente válido en 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)

Acepta cualquier tipo e invierte los bytes en el argumento pasado. Usos de ejemplo:

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

Que imprime:

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

Lo anterior es perfectamente compatible con copiar / pegar, pero están sucediendo muchas cosas aquí, así que desglosaré cómo funciona pieza por pieza:

Lo primero notable es que toda la macro está encerrada en un do while(0) bloque. Este es un idioma común para permitir el uso normal de punto y coma después de la macro.

El siguiente es el uso de una variable llamada REVERSE_BYTES como forcontador del bucle. El nombre de la macro en sí se usa como un nombre de variable para garantizar que no entre en conflicto con ningún otro símbolo que pueda estar en el lugar donde se usa la macro. Dado que el nombre se está utilizando dentro de la expansión de la macro, no se expandirá nuevamente cuando se use como nombre de variable aquí.

Dentro del forbucle, se hace referencia a dos bytes y se intercambia XOR (por lo que no se requiere un nombre de variable temporal):

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

__VA_ARGS__representa lo que se le dio a la macro y se usa para aumentar la flexibilidad de lo que se puede pasar (aunque no mucho). La dirección de este argumento se toma y se envía a ununsigned char puntero para permitir el intercambio de sus bytes a través de la []suscripción de la matriz .

El último punto peculiar es la falta de {}frenillos. No son necesarios porque todos los pasos de cada intercambio se unen con el operador de coma , lo que los convierte en una declaración.

Finalmente, vale la pena señalar que este no es el enfoque ideal si la velocidad es una prioridad. Si este es un factor importante, algunas de las macros específicas de tipo o directivas específicas de plataforma a las que se hace referencia en otras respuestas son probablemente una mejor opción. Sin embargo, este enfoque es portátil para todos los tipos, todas las plataformas principales y los lenguajes C y C ++.

Ryan Hilbert
fuente
Encontré esto en algún lugar en algún código. me confundió muchísimo. Gracias por la explicación. Sin embargo, ¿por qué el uso de __VA_ARGS__?
asr9
0

Wow, no podía creer algunas de las respuestas que he leído aquí. En realidad, hay una instrucción en el ensamblaje que hace esto 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 desarmé y miré. La función anterior no tiene prólogo / epílogo, por lo que prácticamente no tiene sobrecarga.

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 demasiado. Mucho mejor que todos los ejemplos anteriores con bucles y plantillas, etc.

Aquí hay algunas advertencias ... En primer lugar, bswap solo está disponible en CPU de 80x486 y superiores. ¿Alguien planea ejecutarlo en un 386?!? Si es así, aún puede reemplazar bswap con ...

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

Además, 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 compilaciones x64. En ese caso, tendrás que usar los intrínsecos del compilador.

El soldador
fuente
1
_byteswap_ulongy _uint64(por ejemplo, en la respuesta aceptada) ambos compilan para usar la bswapinstrucción. Me sorprendería pero me interesaría saber si este asm es mucho más rápido ya que solo omite el prólogo / epílogo: ¿lo comparó?
ZachB
@stdcall La pregunta no pedía una solución portátil ni mencionaba nada sobre una plataforma. Como decía mi respuesta, lo anterior es sobre la forma más rápida de intercambiar endian. Claro, si estás escribiendo esto en una plataforma que no sea X86, entonces esto no va a funcionar, pero como también mencioné, entonces estás limitado a intrínsecos del compilador, si tu compilador los admite.
The Welder
@ZachB En este caso particular, creo que omitir el prólogo y el epílogo le dará un ahorro decente porque esencialmente solo está ejecutando 1 instrucción. El prólogo tendrá que empujar hacia la pila, hacer una resta, establecer el puntero base y luego similar al final. No lo he comparado, pero lo anterior tiene una cadena de dependencia 0 que simplemente no vas a obtener sin que esté desnudo. Tal vez un buen compilador lo alinearía, pero entonces estás en un estadio diferente.
The Welder
2
Quizás. Pero tenga en cuenta que en el caso común de intercambiar una matriz de números, los intrínsecos del compilador discutidos en otras respuestas usarán extensiones SSE / AVX y emitirán PSHUFB, que supera a BSWAP. Ver wm.ite.pl/articles/reverse-array-of-bytes.html
ZachB
En mi humilde opinión, es malo publicar una solución específica de la plataforma, cuando el OP no especificó que solo necesitaban una solución para x86. Y para menospreciar las otras soluciones, cuando la suya no se puede usar en muchos sistemas operativos muy utilizados, como iOS y Android (que usan CPU ARM o MIPS).
Jens Alfke
0

Técnica portátil para implementar accesores endianos no alineados optimizados y optimizados para optimizadores. Funcionan en cada compilador, cada alineación de límites y cada orden de bytes. Estas rutinas no alineadas se complementan o se modifican según el endian nativo y la alineación. Listado parcial pero se entiende la idea. BO * son valores constantes basados ​​en el orden de bytes nativos.

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

Estos typedefs tienen la ventaja de generar errores de compilación si no se usan con los accesores, mitigando así los errores olvidados de los accesos.

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 */
BSalita
fuente
2
Para esta pregunta, la etiqueta C ++ hace la diferencia. Hay muchos comportamientos indefinidos debido a C ++ y la unión.
jww
0

Aquí le mostramos cómo leer un archivo doble almacenado en formato IEEE 754 de 64 bits, incluso si su computadora host usa un sistema diferente.

/*
* 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 ver el resto del conjunto de funciones, incluidas las rutinas de escritura y de enteros, vea mi proyecto github

https://github.com/MalcolmMcLean/ieee754

Malcolm McLean
fuente
0

El intercambio de bytes con un viejo truco de 3 pasos alrededor de un pivote en una función de plantilla proporciona una solución O (ln2) flexible y rápida que no requiere una biblioteca, el estilo aquí también rechaza los tipos de 1 byte:

template<typename T>void swap(T &t){
    for(uint8_t pivot = 0; pivot < sizeof(t)/2; pivot ++){
        *((uint8_t *)&t + pivot) ^= *((uint8_t *)&t+sizeof(t)-1- pivot);
        *((uint8_t *)&t+sizeof(t)-1- pivot) ^= *((uint8_t *)&t + pivot);
        *((uint8_t *)&t + pivot) ^= *((uint8_t *)&t+sizeof(t)-1- pivot);
    }
}
Quinn Carver
fuente
0

Parece que la forma segura sería usar htons en cada palabra. Entonces, si tienes ...

std::vector<uint16_t> storage(n);  // where n is the number to be converted

// the following would do the trick
std::transform(word_storage.cbegin(), word_storage.cend()
  , word_storage.begin(), [](const uint16_t input)->uint16_t {
  return htons(input); });

Lo anterior sería un no-op si estuviera en un sistema big-endian, por lo que buscaría lo que su plataforma use como condición de tiempo de compilación para decidir si htons es un no-op. Es O (n) después de todo. En una Mac, sería algo así como ...

#if (__DARWIN_BYTE_ORDER != __DARWIN_BIG_ENDIAN)
std::transform(word_storage.cbegin(), word_storage.cend()
  , word_storage.begin(), [](const uint16_t input)->uint16_t {
  return htons(input); });
#endif
cicollinas
fuente
0

Si tiene C ++ 17, agregue este encabezado

#include <algorithm>

Use esta función de plantilla para intercambiar los bytes:

template <typename T>
void swapEndian(T& buffer)
{
    static_assert(std::is_pod<T>::value, "swapEndian support POD type only");
    char* startIndex = static_cast<char*>((void*)buffer.data());
    char* endIndex = startIndex + sizeof(buffer);
    std::reverse(startIndex, endIndex);
}

llámalo como:

swapEndian (stlContainer);
Thinkal VB
fuente
-4

Mire hacia arriba un poco, ya que esto es básicamente todo lo que necesita hacer para cambiar de little -> big endian. Luego, dependiendo del tamaño de la broca, cambia la forma en que realiza el cambio de broca.

Barón Rojo
fuente