C – Conversión de matriz de caracteres int sin firmar a sin firmar

6 minutos de lectura

Tengo un número int sin firmar (2 bytes) y quiero convertirlo al tipo de caracteres sin firmar. De mi búsqueda, encuentro que la mayoría de las personas recomiendan hacer lo siguiente:

 unsigned int x;
 ...
 unsigned char ch = (unsigned char)x;

¿Es el enfoque correcto? Pregunto porque el carácter sin firmar es de 1 byte y pasamos de datos de 2 bytes a 1 byte.

Para evitar la pérdida de datos, quiero crear una matriz de caracteres sin firmar[] y guarde los bytes individuales en la matriz. Estoy atascado en lo siguiente:

 unsigned char ch[2];
 unsigned int num = 272;

 for(i=0; i<2; i++){
      // how should the individual bytes from num be saved in ch[0] and ch[1] ??
 }

También, ¿Cómo convertiríamos el carácter sin firmar?[2] volver a int sin firmar.

Muchas gracias.

  • posible duplicado de byte corto firmado en c ++

    – Pablo R.

    25 de abril de 2012 a las 16:34

Puedes usar memcpy en ese caso:

memcpy(ch, (char*)&num, 2); /* although sizeof(int) would be better */

Además, ¿cómo se convertiría el carácter sin firmar?[2] volver a int sin firmar.

De la misma manera, simplemente invierta los argumentos de memcpy.

Qué tal si:

ch[0] = num & 0xFF;
ch[1] = (num >> 8) & 0xFF;

La operación inversa se deja como ejercicio.

¿Qué tal usar una unión?

union {
    unsigned int num;
    unsigned char ch[2];
}  theValue;

theValue.num = 272;
printf("The two bytes: %d and %d\n", theValue.ch[0], theValue.ch[1]);

Realmente depende de tu objetivo: ¿por qué quieres convertir esto en un unsigned char? Dependiendo de la respuesta a eso, hay algunas maneras diferentes de hacer esto:

  • Truncar: Esto es lo que se recomendó. Si solo está tratando de introducir datos en una función que requiere un unsigned charsimplemente lanza uchar ch = (uchar)x (pero, por supuesto, tenga cuidado con lo que sucede si su int es demasiado grande).

  • endiano específico: use esto cuando su destino requiera un formato específico. Por lo general, al código de red le gusta todo lo que se convierte en matrices de caracteres big endian:

    int n = sizeof x;
    for(int y=0; n-->0; y++)
        ch[y] = (x>>(n*8))&0xff;
    

    voluntad hace eso.

  • Máquina endiana. Úselo cuando no haya un requisito de endianness y los datos solo se produzcan en una máquina. El orden de la matriz cambiará en diferentes arquitecturas. La gente suele encargarse de esto con unions:

    union {int x; char ch[sizeof (int)];} u;
    u.x = 0xf00
    //use u.ch 
    

    con memcpy:

    uchar ch[sizeof(int)];
    memcpy(&ch, &x, sizeof x);
    

    o con la conversión simple siempre peligrosa (que es un comportamiento indefinido y falla en numerosos sistemas):

    char *ch = (unsigned char *)&x;
    

avatar de usuario
Agente_L

Por supuesto, una matriz de caracteres lo suficientemente grande como para contener un valor mayor tiene que ser exactamente tan grande como este valor en sí. Entonces, simplemente puede pretender que este valor más grande ya es una matriz de caracteres:

unsigned int x = 12345678;//well, it should be just 1234.
unsigned char* pChars;

pChars = (unsigned char*) &x;

pChars[0];//one byte is here
pChars[1];//another byte here

(Una vez que comprenda lo que está sucediendo, puede hacerlo sin ninguna variable, todo solo con casting)

  • Si 12345678 encaja unsigned int y sizeof(unsigned int) == 2después CHAR_BIT es más grande de lo normal 😉

    –Steve Jessop

    25 de abril de 2012 a las 16:39

  • ¡Culpo a la sociedad de 32 bits por consentirme!

    – Agente_L

    25 de abril de 2012 a las 16:44

Solo necesita extraer esos bytes usando bitwise & operator. OxFF es una máscara hexadecimal para extraer un byte. Mire varias operaciones de bits aquí: http://www.catonmat.net/blog/low-level-bit-hacks-you-absolutely-must-know/

Un ejemplo de programa es el siguiente:

#include <stdio.h>

int main()
{
    unsigned int i = 0x1122;
    unsigned char c[2];

    c[0] = i & 0xFF;
    c[1] = (i>>8) & 0xFF;

    printf("c[0] = %x \n", c[0]);
    printf("c[1] = %x \n", c[1]);
    printf("i    = %x \n", i);

    return 0;
}

Producción:

$ gcc 1.c 
$ ./a.out 
c[0] = 22 
c[1] = 11 
i    = 1122 
$

  • Si 12345678 encaja unsigned int y sizeof(unsigned int) == 2después CHAR_BIT es más grande de lo normal 😉

    –Steve Jessop

    25 de abril de 2012 a las 16:39

  • ¡Culpo a la sociedad de 32 bits por consentirme!

    – Agente_L

    25 de abril de 2012 a las 16:44

avatar de usuario
jonathask

Apoyando la sugerencia de @abelenky, usando un union sería una forma más a prueba de fallas de hacer esto.

union unsigned_number {
    unsigned int  value;        // An int is 4 bytes long
    unsigned char index[4];     // A char is 1 byte long
};

Las características de este tipo es que el compilador asignará memoria solo para el miembro más grande de nuestra estructura de datos. unsigned_numberque en este caso va a ser 4 bytes – ya que ambos miembros (valor e índice) tienen el mismo tamaño. ¿Lo habías definido como un struct en cambio, tendríamos 8 bytes asignado en la memoria, ya que el compilador hace su asignación para todos los miembros de un struct.

Además, y aquí es donde se resuelve su problema, los miembros de un union estructura de datos todos comparten la misma ubicación de memorialo que significa que todos se refieren a los mismos datos; piense en eso como un enlace fijo en los sistemas GNU/Linux.

Entonces tendríamos:

union unsigned_number my_number;

// Assigning decimal value 202050300 to my_number
// which is represented as 0xC0B0AFC in hex format
my_number.value = 0xC0B0AFC;   // Representation:  Binary - Decimal
                               // Byte 3: 00001100 - 12
                               // Byte 2: 00001011 - 11
                               // Byte 1: 00001010 - 10
                               // Byte 0: 11111100 - 252

// Printing out my_number one byte at time
for (int i = 0; i < (sizeof(my_number.value)); i++)
{
    printf("index[%d]: %u, 0x%x\n", \
        i, my_number.index[i], my_number.index[i]);
}

// Printing out my_number as an unsigned integer
printf("my_number.value: %u, 0x%x", my_number.value, my_number.value);

Y la salida va a ser:

index[0]: 252, 0xfc
index[1]: 10, 0xa
index[2]: 11, 0xb
index[3]: 12, 0xc
my_number.value: 202050300, 0xc0b0afc

Y en cuanto a su pregunta final, no tendríamos que convertir de carácter sin firmar de regreso int sin firmar ya que los valores ya están ahí. Solo tienes que elegir por qué vía quieres acceder

Nota 1: Estoy usando un número entero de 4 bytes para facilitar la comprensión del concepto. Para el problema que presentaste debes usar:

union unsigned_number {
    unsigned short int value;        // A short int is 2 bytes long
    unsigned char      index[2];     // A char is 1 byte long
};

Nota 2: he asignado byte 0 a 252 para señalar la característica no firmada de nuestro index campo. ¿Fue declarado como un signed chartendríamos index[0]: -4, 0xfc como salida.

¿Ha sido útil esta solución?