¿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í.
c++
endianness
Uhall
fuente
fuente
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.Respuestas:
Si está utilizando Visual C ++, haga lo siguiente: incluye intrin.h y llama a las siguientes funciones:
Para números de 16 bits:
Para números de 32 bits:
Para números de 64 bits:
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í :
(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
fuente
__builtin_bswapX
solo está disponible desde GCC-4.3 en adelantehtonl
,htons
, etc Usted tiene que saber desde el contexto de su situación cuando en realidad intercambiar los bytes.htonl
yntohl
sin 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 enhtonl
yntohl
.Simplemente pon:
Uso:
swap_endian<uint32_t>(42)
.fuente
De The Byte Order Fallacy de Rob Pike:
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
data
fuera una matriz deunsigned char
ouint8_t
. Usarsigned char
ochar
(si está firmado) resultará endata[x]
ser promovido a un número entero ydata[x] << 24
potencialmente cambiar un 1 al bit de signo que es UB.fuente
Si está haciendo esto para fines de compatibilidad de red / host, debe usar:
Si está haciendo esto por alguna otra razón, una de las soluciones byte_swap presentadas aquí funcionaría bien.
fuente
htonl
yntohl
no puedo ir a little endian en una plataforma big-endian.Tomé algunas sugerencias de esta publicación y las reuní para formar esto:
fuente
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:
fuente
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.fuente
Lo hemos hecho con plantillas. Podrías hacer algo como esto:
fuente
Si está haciendo esto para transferir datos entre diferentes plataformas, mire las funciones ntoh y hton.
fuente
De la misma manera que lo haces en C:
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.
fuente
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:
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.
fuente
htonl
y 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.)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:
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}.
fuente
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 ++:Salida:
fuente
me gusta este, solo por estilo :-)
fuente
char[]
decir 'Error: no se permite el tipo incompleto'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?
¡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 auint8_t
. Por lo tanto, lo correcto es copiar esto en una matriz de caracteres, intercambiarlo y luego copiarlo de nuevo (así que noreinterpret_cast
). Tenga en cuenta que los compiladores son lo suficientemente inteligentes como para convertir lo que hizo de nuevo areinterpret_cast
si son capaces de acceder a bytes individuales independientemente de la alineación.Para usar esta función :
y ahora
x
es diferente en endianness.fuente
new
/delete
para asignar un búfer para esto?!?sizeof(var)
es una constante de tiempo de compilación, por lo que podría hacerlochar varSwapped[sizeof(var)]
. O podrías hacerchar *p = reinterpret_cast<char*>(&var)
e intercambiar en el lugar.for(size_t i = 0 ; i < sizeof(var) ; i++)
lugar de astatic_cast<long>
. (O, en realidad, el intercambio en el lugar usará un ascendente y descendentechar*
para que desaparezca de todos modos).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:
fuente
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 ->
fuente
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.
Descargo de responsabilidad: no he intentado compilar esto o probarlo todavía.
fuente
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:
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:
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:
Si lo desea, puede escribir eso como un bucle. No será eficiente, pero solo por diversión:
Y para completar, aquí está la versión simplificada de 32 bits de la primera forma:
fuente
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.
fuente
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.
fuente
Usando los códigos a continuación, puede intercambiar entre BigEndian y LittleEndian fácilmente
fuente
Recientemente escribí una macro para hacer esto en C, pero es igualmente válido en C ++:
Acepta cualquier tipo e invierte los bytes en el argumento pasado. Usos de ejemplo:
Que imprime:
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
comofor
contador 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
for
bucle, se hace referencia a dos bytes y se intercambia XOR (por lo que no se requiere un nombre de variable temporal):__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 ++.
fuente
__VA_ARGS__
?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 ...
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.
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 ...
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.
fuente
_byteswap_ulong
y_uint64
(por ejemplo, en la respuesta aceptada) ambos compilan para usar labswap
instrucció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ó?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.
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.
fuente
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.
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
fuente
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:
fuente
Parece que la forma segura sería usar htons en cada palabra. Entonces, si tienes ...
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 ...
fuente
Si tiene C ++ 17, agregue este encabezado
Use esta función de plantilla para intercambiar los bytes:
llámalo como:
fuente
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.
fuente