[C con Clase] como crear cadena de bytes?

Steven Davidson steven en conclase.net
Vie Mar 9 14:09:54 CET 2007


Hola Javi,

El pasado 2007-03-09 09:10:24, H escribió:

H> Hola gente!
H> Al igual es una pregunta un tanto tonta, pero a ver si alguien puede echarme 
H> una mano.

Veamos el problema.

H> El caso es que quiero concatenar en un variable del tipo "unsigned char*" 
H> varios bytes.
H> Por ejemplo algo como esto:
H> unsigned char* buf;
H> int i=0,j=1;
H> char* str="Cadena";
H> asprintf((char*) buf,"%d%d%s",i,j,str);
H> Con esto lo aue quiero es crear una cadena de bytes que luego pueda enviar 
H> por un socket, pero mi problema esta en que no me crea la cadena de bytes 
H> corretamente ya que en el buffer me pone el valor ascii de los numeros que 
H> contiene i y j y no el valor en bytes, es decir, que en lugar de poner "00 
H> 00 00 01", me escribe "31", en lugar de 4 bytes, pone 1 byte.

Esto ocurre porque así es como funciona 'sprintf()'. Esta función aplica criterios de conversión para representar los valores a cadenas de caracteres.

H> Entonces la otra manera que he visto de hacerlo es usando la funcion 
H> "memcpy", pero me resulta un poco mas "costoso" poraue tengo q insertar uno 
H> a uno. Ejemplo:

Efectivamente, 'memcpy()' te puede servir, si quieres usarla.

H> buf = (unsigned char*) malloc(4+4+6);
H> memcpy(buf,i,4);
H> memcpy(buf+4,j,4);
H> memcpy(buf+8,str,6);

No aconsejo escribir explícitamente estas cantidades, ya que en C/C++, los tipos de datos no tienen cantidades fijas, a excepción de 'char' que siempre es 1 byte. Deberías usar el operador 'sizeof'. Por ejemplo,

buf = (unsigned char *) malloc( sizeof i + sizeof j + sizeof str - 1 );
memcpy( buf, i, sizeof i );
memcpy( buf + sizeof i, j, sizeof j );
memcpy( buf + sizeof i + sizeof j, str, sizeof str - 1 );

Como lo anterior es un poco engorroso, sugiero usar un puntero para ir aumentándolo a medida que vayamos agregando datos al búfer. Por ejemplo,

buf = (unsigned char *) malloc( sizeof i + sizeof j + sizeof str - 1 );
unsigned char *ptr = buf;

memcpy( ptr, i, sizeof i );
memcpy( ptr += sizeof i, j, sizeof j );
memcpy( ptr += sizeof j, str, sizeof str - 1 );
ptr += sizeof str - 1;

Luego, usamos 'bufer' para enviar la información por el "enchufe" (o socket) porque apunta al comienzo del bloque de memoria.

H> Assi si me escribe correctamente los bytes, pero resulta algo mas "complejo" 
H> de escribirlo.
H> Entonces, alguien podria decirme si hay alguna manera de hacerlo mas simple?
H> Tambien he probado de usar un stringstream y el resultado es igual que con 
H> el asprintf.

Nuevamente, la clase 'stringstream' interpreta los valores para representarlos como cadenas de caracteres.


En cuanto a tu problema, no hay mucho más que hacer que copiar la información como lo has hecho con 'memcpy()'. También podríamos hacer las asignaciones directamente. Por ejemplo,

buf = (unsigned char *) malloc( sizeof i + sizeof j + sizeof str - 1 );
unsigned char *ptr = buf;

*((int *)ptr)++ = i;   // Implícitamente: ptr += sizeof i;

*((int *)ptr)++ = j;   // Implícitamente: ptr += sizeof j;

strcpy( ptr, str, sizeof str - 1 );
ptr += sizeof str - 1;

Lo que estamos haciendo es "engañar" al compilador para que maneje el puntero como si fuese del tipo del valor a asignar.

Otra posibilidad es usar un 'struct' para agrupar toda la información contiguamente. Ten cuidado con la alineación de las estructuras, ya que es posible que se introduzcan bytes no usados en la representación interna de la estructura. Luego, copiamos con 'memcpy()' toda la información en el búfer para luego enviar su contenido por el "socket".

Por último, una técnica que puede o no funcionar es colocar todas las declaraciones pertinentes contiguamente. Por ejemplo,

int i,j;
char str[6];

El compilador puede organizar estas declaraciones contiguamente en memoria. Por lo tanto, ya tienes la información estructurada como un array. Si esto ocurre, entonces puedes usar un puntero al primer elemento. Por ejemplo,

unsigned char *ptr = (unsigned char *) &i;

Y luego, simplemente invocamos la función para enviar:

EnviarPorSocket( socket, ptr, sizeof i + sizeof j + sizeof str - 1 );


De todas maneras, todo esto es para enviar toda la información de una sola vez. Optativamente, podríamos hacer varias llamadas para enviar cada dato por separado. Por ejemplo,

EnviarPorSocket( socket, &i, sizeof i );
EnviarPorSocket( socket, &j, sizeof j );
EnviarPorSocket( socket, str, sizeof str - 1 );

Seguramente, hacer esto ralentizará el proceso, pero si no quieres estar copiando la información en un búfer que previamente tienes que crear, quizá te convenga.


Espero que esto te sirva.

Steven


Más información sobre la lista de distribución Cconclase