www tuto try tipos funcion dev casteo cast c++ pointers casting

tuto - try on c++



C++: cómo convertir 2 bytes en una matriz a un corto sin firmar (11)

He estado trabajando en una aplicación heredada de C ++ y estoy definitivamente fuera de mi zona de confort (algo bueno). Me preguntaba si alguien por ahí sería tan amable de darme algunos consejos (juego de palabras).

Necesito convertir 2 bytes en una matriz de caracteres sin signo a un corto sin signo. Los bytes son consecutivos.

Para un ejemplo de lo que estoy tratando de hacer:

Recibo una cadena de un socket y la coloco en una matriz de caracteres sin signo. Puedo ignorar el primer byte y luego los siguientes 2 bytes deben convertirse a un char sin signo. Esto solo se mostrará en Windows, por lo que no hay problemas Big / Little Endian (que yo sepa).

Esto es lo que tengo ahora (no funciona obviamente):

//packetBuffer is an unsigned char array containing the string "123456789" for testing //I need to convert bytes 2 and 3 into the short, 2 being the most significant byte //so I would expect to get 515 (2*256 + 3) instead all the code I have tried gives me //either errors or 2 (only converting one byte unsigned short myShort; myShort = static_cast<unsigned_short>(packetBuffer[1])


¿Nadie vio que la entrada era una cadena?

/* If it is a string as explicitly stated in the question. */ int byte1 = packetBuffer[1] - ''0''; // convert 1st byte from char to number. int byte2 = packetBuffer[2] - ''0''; unsigned short result = (byte1 * 256) + byte2; /* Alternatively if is an array of bytes. */ int byte1 = packetBuffer[1]; int byte2 = packetBuffer[2]; unsigned short result = (byte1 * 256) + byte2;

Esto también evita los problemas de alineación que la mayoría de las otras soluciones pueden tener en ciertas plataformas. Nota Un short tiene al menos dos bytes. La mayoría de los sistemas te darán un error de memoria si tratas de desviar un puntero corto que no está alineado con 2 bytes (o cualquiera que sea el tamaño de (corto) en tu sistema).


No debe convertir un puntero de char sin signo en un puntero corto sin signo (para ese caso, convertirlo de un puntero de tipo de datos más pequeño a un tipo de datos más grande). Esto se debe a que se supone que la dirección se alineará correctamente. Un mejor enfoque es cambiar los bytes en un objeto corto real sin signo o memcpy en un conjunto corto sin signo.

Sin duda, puede ajustar la configuración del compilador para evitar esta limitación, pero esto es algo muy sutil que se romperá en el futuro si el código se pasa y se reutiliza.


Probablemente esté muy por debajo de lo que le importa, pero tenga en cuenta que puede obtener fácilmente un acceso no alineado para hacer esto. x86 es indulgente y el aborto que causa el acceso no alineado se detectará internamente y terminará con una copia y devolución del valor para que su aplicación no sepa nada diferente (aunque es significativamente más lento que un acceso alineado). Sin embargo, si este código se ejecutará en un no-x86 (no mencionas la plataforma de destino, entonces asumo que Windows de escritorio x86), al hacerlo se cancelarán los datos del procesador y tendrás que copiar manualmente los datos a una dirección alineada antes de tratar de lanzarlo.

En resumen, si va a acceder de este modo, puede considerar realizar ajustes en el código para no tener lecturas desalineadas y verá un beneficio de rendimiento.


la conversión estática tiene una sintaxis diferente, además de que necesita trabajar con punteros, lo que quiere hacer es:

unsigned short *myShort = static_cast<unsigned short*>(&packetBuffer[1]);


char packetBuffer[] = {1, 2, 3}; unsigned short myShort = * reinterpret_cast<unsigned short*>(&packetBuffer[1]);

Yo (tuve que) hacer esto todo el tiempo. Big Endian es un problema obvio. Lo que realmente le conseguirá son datos incorrectos cuando la máquina detesta las lecturas desalineadas. (y escribe).

Es posible que desee escribir un modelo de prueba y una afirmación para ver si se lee correctamente. Por lo tanto, cuando se ejecuta en una máquina endian grande o, lo que es más importante, una máquina que no le gusta lee desalineada, se producirá un error afirmativo en lugar de un extraño ''error'' difícil de rastrear;)


unsigned short myShort = *(unsigned short *)&packetBuffer[1];


Bueno, estás ampliando el char en un valor corto. Lo que quiere es interpretar dos bytes como un corto. static_cast no puede static_cast desde unsigned char* a unsigned short* . Tienes que convertir a void* , luego a unsigned short* :

unsigned short *p = static_cast<unsigned short*>(static_cast<void*>(&packetBuffer[1]));

Ahora, puede desreferenciar p y obtener el valor corto. Pero el problema con este enfoque es que lanzas desde char * sin signo, a vacío * y luego a otro tipo diferente. El Estándar no garantiza que la dirección siga siendo la misma (y además, desreferenciar ese puntero sería un comportamiento indefinido). Un mejor enfoque es usar el cambio de bit, que siempre funcionará:

unsigned short p = (packetBuffer[1] << 8) | packetBuffer[2];


En Windows puede usar:

unsigned short i = MAKEWORD(lowbyte,hibyte);


Me doy cuenta de que este es un hilo viejo, y no puedo decir que probé todas las sugerencias hechas aquí. Me estoy haciendo sentir cómodo con mfc, y estaba buscando una manera de convertir un uint a dos bytes, y viceversa en el otro extremo de un socket.

Hay muchos ejemplos de cambios que puedes encontrar en la red, pero ninguno de ellos parecía funcionar realmente. Muchos de los ejemplos parecen demasiado complicados; Quiero decir que solo estamos hablando de tomar 2 bytes de un error, enviándolos por el cable y volviéndolos a conectar en el otro extremo, ¿verdad?

Esta es la solución que finalmente se me ocurrió:

class ByteConverter { public: static void uIntToBytes(unsigned int theUint, char* bytes) { unsigned int tInt = theUint; void *uintConverter = &tInt; char *theBytes = (char*)uintConverter; bytes[0] = theBytes[0]; bytes[1] = theBytes[1]; } static unsigned int bytesToUint(char *bytes) { unsigned theUint = 0; void *uintConverter = &theUint; char *thebytes = (char*)uintConverter; thebytes[0] = bytes[0]; thebytes[1] = bytes[1]; return theUint; } };

Usado así:

unsigned int theUint; char bytes[2]; CString msg;
ByteConverter::uIntToBytes(65000,bytes); theUint = ByteConverter::bytesToUint(bytes);
msg.Format(_T("theUint = %d"), theUint); AfxMessageBox(msg, MB_ICONINFORMATION | MB_OK);

Espero que esto ayude a alguien.


El cambio de bit anterior tiene un error:

unsigned short p = (packetBuffer[1] << 8) | packetBuffer[2];

si packetBuffer está en bytes (8 bits de ancho), el cambio anterior puede convertir y convertirá packetBuffer en cero, dejándote solo con packetBuffer[2];

A pesar de esto, esto aún se prefiere a los indicadores. Para evitar el problema anterior, desperdicio unas pocas líneas de código (aparte de la optimización bastante literal-cero) resulta en el mismo código de máquina:

unsigned short p; p = packetBuffer[1]; p <<= 8; p |= packetBuffer[2];

O para guardar algunos ciclos de reloj y no desplazar los bits del final:

unsigned short p; p = (((unsigned short)packetBuffer[1])<<8) | packetBuffer[2];

Debe tener cuidado con los punteros, el optimizador lo morderá, así como las alineaciones de memoria y una larga lista de otros problemas. Sí, hecho bien es más rápido, hecho mal, el error puede permanecer por mucho tiempo y golpear cuando menos se desee.

Digamos que era flojo y quería hacer algunas operaciones matemáticas de 16 bits en una matriz de 8 bits. (pequeño endian)

unsigned short *s; unsigned char b[10]; s=(unsigned short *)&b[0]; if(b[0]&7) { *s = *s+8; *s &= ~7; } do_something_With(b); *s=*s+8; do_something_With(b); *s=*s+8; do_something_With(b);

No hay garantía de que un compilador perfectamente libre de errores cree el código que esperas. El byte array b enviado a la función do_something_with() nunca puede ser modificado por las operaciones de *s . Nada en el código anterior dice que debería. Si no optimiza su código, es posible que nunca vea este problema (hasta que alguien optimice o cambie los compiladores o las versiones del compilador). Si usa un depurador, es posible que nunca vea este problema (hasta que sea demasiado tarde).

El compilador no ve la conexión entre s y b, son dos elementos completamente separados. El optimizador puede elegir no escribir *s en la memoria porque ve que *s tiene varias operaciones, por lo que puede mantener ese valor en un registro y solo guardarlo en la memoria al final (si es que lo hace).

Hay tres formas básicas de corregir el problema del puntero anterior:

  1. Declare s como volátil.
  2. Usa una unión.
  3. Use una función o funciones siempre que cambie de tipo.

Quizás esta sea una solución muy tardía, pero solo quiero compartirla contigo. Cuando desee convertir primitivas u otros tipos, puede usar la unión. Vea abajo:

union CharToStruct { char charArray[2]; unsigned short value; }; short toShort(char* value){ CharToStruct cs; cs.charArray[0] = value[1]; // most significant bit of short is not first bit of char array cs.charArray[1] = value[0]; return cs.value; }

Cuando creas una matriz con los valores hexadecimales inferiores y llamas a la función Corta, obtendrás un valor corto con 3.

char array[2]; array[0] = 0x00; array[1] = 0x03; short i = toShort(array); cout << i << endl; // or printf("%h", i);