Alex Xander
Necesito escribir una función para convertir un entero big endian en un entero little endian en C. No puedo usar ninguna función de biblioteca. ¿Cómo haría esto?
mensaje de sam
Suponiendo que lo que necesita es un simple intercambio de bytes, intente algo como
Conversión de 16 bits sin firmar:
swapped = (num>>8) | (num<<8);
Conversión de 32 bits sin firmar:
swapped = ((num>>24)&0xff) | // move byte 3 to byte 0
((num<<8)&0xff0000) | // move byte 1 to byte 2
((num>>8)&0xff00) | // move byte 2 to byte 1
((num<<24)&0xff000000); // byte 0 to byte 3
Esto intercambia los órdenes de bytes de las posiciones 1234 a 4321. Si su entrada fue 0xdeadbeef
un intercambio endian de 32 bits podría tener una salida de 0xefbeadde
.
El código anterior debe limpiarse con macros o al menos constantes en lugar de números mágicos, pero espero que ayude como está
EDITAR: como señaló otra respuesta, existen alternativas específicas de plataforma, sistema operativo y conjunto de instrucciones que pueden ser MUCHO más rápidas que las anteriores. En el kernel de Linux hay macros (cpu_to_be32 por ejemplo) que manejan el endian bastante bien. Pero estas alternativas son específicas de sus entornos. En la práctica, la endianidad se trata mejor utilizando una combinación de enfoques disponibles.
-
+1 por mencionar métodos específicos de plataforma/hardware. Los programas siempre se ejecutan en algún hardware y las características del hardware siempre son más rápidas.
– eonil
31 de julio de 2012 a las 6:47
-
si la conversión de 16 bits se realiza como
((num & 0xff) >> 8) | (num << 8)
gcc 4.8.3 genera un solorol
instrucción. Y si la conversión de 32 bits se escribe como((num & 0xff000000) >> 24) | ((num & 0x00ff0000) >> 8) | ((num & 0x0000ff00) << 8) | (num << 24)
el mismo compilador genera un únicobswap
instrucción.– usuario666412
30 de abril de 2015 a las 16:51
-
No sé qué tan eficiente es esto, pero cambié el orden de los bytes con campos de bits como este:
struct byte_t reverse(struct byte_t b) { struct byte_t rev; rev.ba = b.bh; rev.bb = b.bg; rev.bc = b.bf; rev.bd = b.be; rev.be = b.bd; rev.bf = b.bc; rev.bg = b.bb; rev.bh = b.ba; return rev;}
donde este es un campo de bits con 8 campos de 1 bit cada uno. Pero no estoy seguro si eso es tan rápido como las otras sugerencias. Para ints use elunion { int i; byte_t[sizeof(int)]; }
para invertir byte por byte en el entero.– Ilian Zaprianov
26/09/2016 a las 22:47
-
Creo que la expresión debe ser: (num >> 8) | (num > 8) | (num
– jscom
24 de febrero de 2017 a las 17:14
-
@IlianZapryanov Tal vez +1 para mayor claridad, pero usar campos de bits en C así es probablemente la forma menos eficiente de hacerlo.
– sherrellbc
26 sep 2019 a las 22:47
Amir Mgh
Incluyendo:
#include <byteswap.h>
puede obtener una versión optimizada de las funciones de intercambio de bytes dependientes de la máquina. Luego, puede usar fácilmente las siguientes funciones:
__bswap_32 (uint32_t input)
o
__bswap_16 (uint16_t input)
-
Gracias por tu respuesta, pero no puedo usar ninguna función de biblioteca.
– Mark Ransom
5 de agosto de 2011 a las 19:03
-
Tiene que leer
#include <byteswap.h>
, vea el comentario en el propio archivo .h. Esta publicación contiene información útil, así que voté a favor a pesar de que el autor ignoró el requisito de OP de no usar una función lib.– Eli Rosencruft
20 de mayo de 2012 a las 12:48
-
De hecho, las funciones __bswap_32/__bswap_16 son macros y no funciones de biblioteca, otra razón para votar a favor.
– Eli Rosencruft
20 de mayo de 2012 a las 13:56
-
Tengo entendido que no se garantiza que este encabezado exista para todos los sistemas operativos en todas las arquitecturas. Todavía tengo que encontrar una forma portátil de lidiar con los problemas de endian.
–Edward Falk
18 de febrero de 2013 a las 23:04
-
#include <byteswap.h>
no es parte de la biblioteca estándar de C.– chux – Reincorporar a Monica
02/04/2018 a las 13:39
chmike
#include <stdint.h>
//! Byte swap unsigned short
uint16_t swap_uint16( uint16_t val )
{
return (val << 8) | (val >> 8 );
}
//! Byte swap short
int16_t swap_int16( int16_t val )
{
return (val << 8) | ((val >> 8) & 0xFF);
}
//! Byte swap unsigned int
uint32_t swap_uint32( uint32_t val )
{
val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF );
return (val << 16) | (val >> 16);
}
//! Byte swap int
int32_t swap_int32( int32_t val )
{
val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF );
return (val << 16) | ((val >> 16) & 0xFFFF);
}
Actualizar : Añadido intercambio de bytes de 64 bits
int64_t swap_int64( int64_t val )
{
val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL );
val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL );
return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL);
}
uint64_t swap_uint64( uint64_t val )
{
val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL );
val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL );
return (val << 32) | (val >> 32);
}
-
Para el
int32_t
yint64_t
variantes, ¿cuál es el razonamiento detrás del enmascaramiento de... & 0xFFFF
y... & 0xFFFFFFFFULL
? ¿Está pasando algo con la extensión de signo aquí que no estoy viendo? Además, ¿por qué esswap_int64
regresandouint64_t
? ¿No debería ser así?int64_t
?– bgoodr
2 de noviembre de 2012 a las 14:57
-
El swap_int64 que devuelve un uint64 es de hecho un error. El enmascaramiento con valores int con signo es, de hecho, para eliminar el signo. Al desplazar a la derecha, se inyecta el bit de signo de la izquierda. Podríamos evitar esto simplemente llamando a la operación de intercambio int sin firmar.
– chmike
3 de noviembre de 2012 a las 15:37
-
Gracias. Es posible que desee cambiar el tipo de valor devuelto para
swap_int64
en tu respuesta +1 por la respuesta útil, ¡por cierto!– bgoodr
4 de noviembre de 2012 a las 17:18
-
¿Es el bit a bit y el valor endian dependiente?
– MarcusJ
4 mayo 2015 a las 21:57
-
El
LL
son innecesarios en(u)swap_uint64()
muy parecido a unL
no es necesario en(u)swap_uint32()
. ElU
no es necesario enuswap_uint64()
al igual que elU
no es necesario enuswap_uint32()
– chux – Reincorporar a Monica
02/04/2018 a las 13:10
miguel j
Aquí hay una versión bastante genérica; No lo he compilado, por lo que probablemente haya errores tipográficos, pero deberías hacerte una idea,
void SwapBytes(void *pv, size_t n)
{
assert(n > 0);
char *p = pv;
size_t lo, hi;
for(lo=0, hi=n-1; hi>lo; lo++, hi--)
{
char tmp=p[lo];
p[lo] = p[hi];
p[hi] = tmp;
}
}
#define SWAP(x) SwapBytes(&x, sizeof(x));
NÓTESE BIEN: Esto es no optimizado para la velocidad o el espacio. Está destinado a ser claro (fácil de depurar) y portátil.
Actualización 2018-04-04
Se agregó la afirmación () para atrapar el caso no válido de n == 0, como lo detectó el comentarista @chux.
Si necesita macros (por ejemplo, sistema integrado):
#define SWAP_UINT16(x) (((x) >> 8) | ((x) << 8))
#define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))
-
Estas macros están bien, pero ((x) >> 24) fallará cuando un entero con signo esté entre 0x80000000 y 0xffffffff. Es una buena idea usar AND bit a bit aquí. Nota: ((x) > 8) también fallará si los 16 bits altos son distintos de cero (o se proporciona un valor de 16 bits con signo).
– usuario1985657
19 de noviembre de 2014 a las 14:59
-
@PacMan: estas macros están destinadas a intercambiar no firmado enteros solamente. Por eso existe el
UINT
en su nombre– kol
19 de noviembre de 2014 a las 19:06
-
Sí, cierto, perdón por el ruido. ¿No sería mejor incrustar un typecast?
– usuario1985657
19 noviembre 2014 a las 20:34
Editar: Estas son funciones de biblioteca. Seguirlos es la forma manual de hacerlo.
Estoy absolutamente asombrado por la cantidad de personas que desconocen __byteswap_ushort, __byteswap_ulong y __byteswap_uint64. Seguro que son específicos de Visual C++, pero compilan un código delicioso en arquitecturas x86/IA-64. 🙂
He aquí un uso explícito de la bswap
instrucción, sacado de esta pagina. Tenga en cuenta que la forma intrínseca anterior siempre ser más rápido que estosolo lo agregué para dar una respuesta sin una rutina de biblioteca.
uint32 cq_ntohl(uint32 a) {
__asm{
mov eax, a;
bswap eax;
}
}
-
Estas macros están bien, pero ((x) >> 24) fallará cuando un entero con signo esté entre 0x80000000 y 0xffffffff. Es una buena idea usar AND bit a bit aquí. Nota: ((x) > 8) también fallará si los 16 bits altos son distintos de cero (o se proporciona un valor de 16 bits con signo).
– usuario1985657
19 de noviembre de 2014 a las 14:59
-
@PacMan: estas macros están destinadas a intercambiar no firmado enteros solamente. Por eso existe el
UINT
en su nombre– kol
19 de noviembre de 2014 a las 19:06
-
Sí, cierto, perdón por el ruido. ¿No sería mejor incrustar un typecast?
– usuario1985657
19 noviembre 2014 a las 20:34
Sueño relajado
Como una broma:
#include <stdio.h>
int main (int argc, char *argv[])
{
size_t sizeofInt = sizeof (int);
int i;
union
{
int x;
char c[sizeof (int)];
} original, swapped;
original.x = 0x12345678;
for (i = 0; i < sizeofInt; i++)
swapped.c[sizeofInt - i - 1] = original.c[i];
fprintf (stderr, "%x\n", swapped.x);
return 0;
}
-
HAHAHAHAHA. Jajaja. Ja. ¿Ja? (¿Que broma?)
– usuario3139831
8 de enero de 2015 a las 13:58
-
¿Sacaste esto de algún repositorio fuente de Windows? 🙂
– hohl
30 de noviembre de 2016 a las 14:24
-
¡Nodejs usa esta técnica! github.com/nodejs/node/blob/…
– Justin Moser
25 de marzo de 2017 a las 22:39
-
curioso de usar
int i, size_t sizeofInt
y no del mismo tipo para ambos.– chux – Reincorporar a Monica
2 de abril de 2018 a las 13:21
un valor de 16 bits? valor de 32 bits? ¿flotar? ¿una matriz?
– John Knoeller
2 de febrero de 2010 a las 5:54
tiempo para elegir una respuesta tal vez?
– Aniket Inge
5 de noviembre de 2012 a las 9:02
Votación para reabrir. Igual que stackoverflow.com/questions/105252/… para C++. Podríamos simplemente editar para que quede más claro.
– Ciro Santilli OurBigBook.com
08/04/2016 a las 20:52
Creo que está lo suficientemente claro. Por favor, abre la pregunta.
– hamidi
20 de enero de 2021 a las 17:23
gcc y g ++ detectan correctamente dichos intercambios y los convierten en una o dos instrucciones, pero probablemente necesite usar
-O3
o al menos-O2
. Entonces deberías escribir una función simple para hacer el intercambio coninline
y automáticamente hará el trabajo por usted.– Alexis Wilke
9 de julio de 2021 a las 1:10