Autor
|
Tema: campos de bits (Leído 13,026 veces)
|
michael_753
Desconectado
Mensajes: 6
|
Hola buenas, estoy arrancando con el tema de campos de bits y tengo el siguiente problema que no lo estaría entendiendo del todo:
Realizar un programa que permita almacenar la información de 10 los diez productos que contiene un depósito en stock, por cada uno se debe almacenar: Código (1 a 10) Cantidad que puede variar entre 0 y 500 Si es Nacional o importado Definir la estructura de forma de optimizar lo más posible el uso de la memoria.
Espero me puedan ayudar a resolverlo para ver como es que se resuelven gracias.
|
|
|
En línea
|
|
|
|
MAFUS
Desconectado
Mensajes: 1.603
|
Te voy a preguntar ¿Qué sabes de los campos de bits? La pregunta tuya es muy abierta. En el foro nos gusta ir al detalle de lo que no sabes, la duda concreta. No nos gusta hacer el ejercicio.
|
|
|
En línea
|
|
|
|
michael_753
Desconectado
Mensajes: 6
|
Hola, ahí mande el código con lo que hice hasta ahora ( en comentarios están las dudas) [code=c]#include <stdio.h> #include <stdlib.h> #include <time.h>
#define max_pro 10
struct ss { unsigned char cod: 4; unsigned short int cant: 9; // declaro los bits que necesito para cada item unsigned char bas : 3;
};
union uu { struct ss s; unsigned char vec[2]; // creo otra variable y mediante la union entre esta variable y la estructura // se comparten la misma direccion de memoria };
struct datos { int codigo; int cantidad;
};
int main() { union uu u; struct datos vec[max_pro];
srand(time(NULL));
for (int i = 0; i < 10; i++) {
vec[i].codigo= i; // lista vacia vec[i].cantidad= 0;
printf("\n\n%d %d", vec[i].codigo, vec[i].cantidad );
}
printf("\n\n");
for ( int i = 0; i < 10; i++) {
do { u.vec[0]= rand()%256; // los guardo en el vector con el que se superpone los campos de bit u.vec[1]= rand()%256;
} while (u.s.cod>10);
printf("\n\n para codigo %d para cant %d ",u.vec[0], u.vec[1]); // para ver los numeros generados
printf("\n\n codigo: %d cantidad: %d \n\n", u.s.cod, u.s.cant ); // aca es mi problema //en el primer byte (del vector) tengo 4 bits para el codigo, //entonces cuando por ejemplo en ese byte hay un 85 = 0101 0101 // para el codigo corresponderia un 5 Y ESO VERIFICA CUANDO LO IMPRIMO, pero // para la cantidad no, me aparece un numero que se repite en todas las posciones vec[u.s.cod].cantidad= u.s.cant;
}
printf("\n\nCOn datos");
for (int i = 0; i < 10; i++) {
printf("\n\n%d %d", vec[i].codigo, vec[i].cantidad );
//sale el mismo numero para todas las posciones
}
return 0; }
[/code]
|
|
|
En línea
|
|
|
|
MAFUS
Desconectado
Mensajes: 1.603
|
Creo que sé lo que intentas hacer y he modificado un poco tu programa. Por cierto, de procedencia dijiste que solo era nacional o extranjera, para ello basta con solo 1 bit, no 3. He puesto algunos comentarios explicativos. #include <stdio.h> #include <stdlib.h> #include <time.h> #define MAX_PROD 10 typedef struct { unsigned codigo: 4; unsigned procedencia: 1; unsigned cantidad: 9; } producto_t; // un unsigned suele ser de 32 bits, mas que suficiente para guardar todo el mapa de bits. // De hecho lo más probable es que el compilador la guarde en memoria con ese tamaño. typedef union { producto_t producto; unsigned repr_interna; } producto; int main() { producto vec[MAX_PROD] = {0}; printf("tamanno del mapa de bits: %lu bytes\n\n", sizeof(producto_t )); // dando datos a los productos for (size_t i = 0; i < 10; i++) { vec[i].producto.codigo = i + 1; // codigo tiene 4 bits: puede guardar desde 0 hasta 15 vec [i ]. producto. procedencia = rand() & 1; // procedencia tiene 1 bit: puede guardar desde 0 hasta 1 vec [i ]. producto. cantidad = rand() % 501; // cantidad tiene 9 bits: puede guardar desde 0 hasta 511 } // es responsabilidad del programador que los datos estén dentro de sus rangos // visualizando los datos for (int i = 0; i < 10; i++) { printf("producto ----------\ncodigo: %u procedencia: %u cantidad: %u\n", vec[i].producto.codigo, vec[i].producto.procedencia, vec[i].producto.cantidad); printf("representacion interna: %04X\n", vec [i ]. repr_interna); } }
Lo curioso es que me ha salido un resultado interesante para mostrar que el compilador hace lo que quiere con las estructuras. Adelantándome a tu respuesta sí, hay formas de obligarlo a que haga las estructuras exactamente como le has dicho, pero de normal intenta optimizarlas al máximo. Uno de los resultados que me ha dado ha sido este: producto ---------- codigo: 8 procedencia: 0 cantidad: 68 representacion interna: 0888Me ha gustado mucho ese 888 hexadecimal pues se puede inferir como están los campos dentro. Veamos: representacion interna: 0 8 8 8 0000 1000 1000 1000
codigo: 8 [ 1000 ] procedencia: 0 [ 0 ] cantidad: 68 [ 001000100 ]
-relleno- cantidad procedencia codigo 00 001000100 0 1000Yo había construido el mapa de bits en el orden codigo -> procedencia -> cantidad y el compilador lo ha construido de forma interna como cantidad -> procedencia -> codigo
|
|
|
En línea
|
|
|
|
michael_753
Desconectado
Mensajes: 6
|
Muchas gracias por tomarte el tiempo, ya pude comprender un poco mas el tema.
|
|
|
En línea
|
|
|
|
michael_753
Desconectado
Mensajes: 6
|
Hola, te hago otra consulta. En código que me pasaste vos lo que hiciste fue como EMPAQUETAR los datos que se van generando correcto ?. Bueno yo lo edite un poco para que me DESEMPAQUETE el numero generado, y el problema es que los valores no verifican. Mando el código y un ejemplo. #include <stdio.h> #include <stdlib.h> #include <time.h> #define MAX_PROD 10 struct s_producto { unsigned char codigo : 4; unsigned short int cantidad: 9; unsigned char procedencia: 1; unsigned char bas: 2; // esto seria basura, osea lo que me queda del byte y que no uso }; union u_producto { struct s_producto producto; unsigned short int empaquetado; // el short int mide 2 bytes, el mismo tamaño que el de los campos sumados }; int main() { union u_producto vec[MAX_PROD] = {0}; printf("tamanno del mapa de bits: %lu bytes\n\n", sizeof(vec ->producto )); // acá me arroja mas bytes de lo que debería, es decir, sumando los bytes me dan 16 = 2 bytes y me imprime 6 bytes. // dando datos a los productos for (size_t i = 0; i < 10; i++) { vec [i ]. empaquetado = rand() % 1000; vec[i].producto.codigo = vec[i].empaquetado; vec[i].producto.cantidad = vec[i].empaquetado; vec[i].producto.procedencia = vec[i].empaquetado; } // visualizando los datos for (int i = 0; i < MAX_PROD; i++) { printf("numero empaquetado: %04X\n", vec [i ]. empaquetado); printf("\ncodigo: %d cantidad: %d procedencia:%d\n\n-------------------------\n", vec[i].producto.codigo, vec[i].producto.cantidad, vec[i].producto.procedencia); } }
Por ejemplo un valor que sale es : 254 = 00FE = 0000 0000 1111 1110 (2 byte) entonces, según como declare los campos: código, cantidad, procedencia, basura código seria: 1110 = 14 // son los 4 bits menos significativos cantidad : 0 0000 1111 = debería ser 15 pero sale 254 (igual que valor que arrojo el rand) procedencia: 0 bas: 00 No se si me explique, espero su respuesta y muchas gracias.
|
|
|
En línea
|
|
|
|
MAFUS
Desconectado
Mensajes: 1.603
|
¿Qué ha pasado? Le has dado unas directrices a C en tu construcción del mapa de bits y ha intentado acomodarlo como ha podido. ¿Cómo lo ha acomodado? Vamos a reducir el programa a la mínima expresión: #include <stdio.h> struct s_producto { unsigned char codigo : 4; unsigned short cantidad: 9; unsigned char procedencia: 1; unsigned char bas: 2; }; union u_producto { struct s_producto producto; unsigned short int empaquetado; }; int main() { union u_producto vec = {0}; printf("tamanno del mapa de bits: %lu bytes\n\n", sizeof(vec. producto)); vec.producto.codigo = 0xF; printf("codigo : %08X\n", vec. empaquetado); vec.producto.procedencia = 0x1; printf("procedencia: %08X\n", vec. empaquetado); vec.producto.cantidad = 0X1FF; printf("cantidad : %08X\n", vec. empaquetado); vec.producto.bas = 0x3; printf("basura : %08X\n", vec. empaquetado); }
Resultado: tamanno del mapa de bits: 6 bytes
codigo : 0000000F procedencia: 0000000F cantidad : 0000000F basura : 0000000F Sólo veo código. Vamos a abrir el rango. En vez de union u_producto { struct s_producto producto; unsigned short int empaquetado; };
ponemos union u_producto { struct s_producto producto; unsigned int empaquetado; };
y resulta en tamanno del mapa de bits: 6 bytes
codigo : 0000000F procedencia: 0000000F cantidad : 01FF000F basura : 01FF000F Podemos ver que alineó codigo en 2 bytes (aunque solo tuviera 4 bits) y cantidad también la ha alineado en otros dos bytes. Ya te digo también que he estado buscando procedencia y basura y no los he encontrado, pero están ahí. En el mejor de los casos está usando un byte en otro sitio para guardar esa información. Por otra parte ocurre una cosa muy interesante. Si juntamos todos los unsigned char dentro del mapa de bits la cosa cambia: struct s_producto { unsigned short cantidad: 9; unsigned char codigo : 4; unsigned char procedencia: 1; unsigned char bas: 2; };
El resultado es el siguiente: tamanno del mapa de bits: 4 bytes
codigo : 000F0000 procedencia: 001F0000 cantidad : 001F01FF basura : 007F01FF Vamos a exprimir un poco más el mapa de bits. ¿Qué pasaría si hubieras usado unsigned short en todos los tipos de datos dentro del mapa? struct s_producto { unsigned short cantidad: 9; unsigned short codigo : 4; unsigned short procedencia: 1; unsigned short bas: 2; };
Esto da como resultado: tamanno del mapa de bits: 2 bytes
codigo : 00001E00 procedencia: 00003E00 cantidad : 00003FFF basura : 0000FFFF De hecho te recomiendo quitar bas, porque no se usa. En estructuras más complicadas sí que se usan bits para alinear la estructura interna, y solo tienen ese propósito, y estos son anónimos, no tienen nombre. Por ejemplo: struct mapa_bits { unsigned dato: 3; unsigned: 4; // hueco de 4 bits sin nombre unsigned dato_2: 7; }
Como dato curioso también existen los anónimos de 0 bits. Estos lo que le indican al compilador es que alinee los campos de bits que le siguen a una potencia de dos. De nuevo todo esto es para facilitar al procesador el movimiento de datos en la memoria. Por ejemplo: struct s_producto { unsigned short cantidad: 9; unsigned short: 0; unsigned short codigo : 4; unsigned short procedencia: 1; };
Me devuelve el siguiente resultado: tamanno del mapa de bits: 4 bytes
codigo : 000F0000 procedencia: 001F0000 cantidad : 001F01FF ¿Qué quiere decir todo esto? No es buena idea usar union de esta forma, es decir, para empaquetar estructuras. Ya has visto que el compilador hace lo que quiere con ellas. Más adelante te encontrarás que incluso guardar una estructura entera en disco o mandarla por la red te dará problemas porque al rescatarla leerás basura. Esto es debido, otra vez, a que el compilador hace lo que quiere con las estructuras y si a ti te las ha colocado de una forma en memoria, a otro puede que tenga sus miembros de forma diferente y por lo tanto se colocarán mal los datos. Pero también puede que en las dos máquinas las estructuras esté construidas igual y consigas bien la información. Esto quiere decir que para serializar y deserializar estructuras manda al stream cada miembro de la estructura, uno a uno, en una secuencia dada. Recupera del stream uno a uno los miembros de la estructura en el mismo orden que los enviaste. Por último, y como ya te habrás dado cuenta, dar diferentes tipos de datos a los miembros de un mapa de bits hace que el compilador intente agruparlos por tipo. Mezclarlos, como has visto, resulta en un desastre. Te recomendaría que uses unsigned y el compilador te lo va a acomodar todo en 32 bits. Si necesitas un control muy fino de bits en memoria deberías pensar en operaciones de desplazamiento de bits junto a operaciones lógicas. Los mapas de bits son más para no gastar mucha memoria en números pequeños y caso es buen ejemplo de ellos. Un dato que perfectamente cabe en 4 bits, otro que cabe en 9 y otro que cabe en 1, puedes meterlo todo en 2 bytes en vez de gastar 12, que sería si los hubieras declarado como tres enteros diferentes. Los campos de bits se ven mucho más en sistemas embebidos y microcontroladores.
|
|
|
En línea
|
|
|
|
michael_753
Desconectado
Mensajes: 6
|
Excelente, ahí hice las correcciones y funciono de maravilla, muchas gracias también por la explicación.
|
|
|
En línea
|
|
|
|
RayR
Desconectado
Mensajes: 243
|
Como ya te recomendaron, es mejor que evites los campos de bits si puedes, ya que no tienes casi ningún control sobre la manera en que el compilador los almacena. También hay unas cosas que creo que vale la pena comentar. Los compiladores tienen ciertas libertades con las estructuras, pero lo que hacen no es aleatorio, y además hay reglas que tienen que seguir. Si dejamos de lado de momento los campos de bits, tenemos algunas garantías con las estructuras: el compilador siempre colocará en memoria los campos en el orden en que los declaramos. Puede haber relleno (padding) entre algunos, pero el orden siempre se respeta. Y la dirección del primer campo es la misma que la de la estructura, es decir, está exactamente al inicio. Luego, hay otras reglas no oficiales, pero que los compiladores en general siguen. Por ejemplo, los compiladores normalmente acomodan los campos de acuerdo a su alineación natural, es decir, en qué direcciones de memoria conviene que estén. Si para lograrlo deben meter bytes de relleno (padding), lo hacen. Esto en general se puede evitar declarándolos de manera que ya estén alineados (no basta con sólo agruparlos por tipo). No sé si quisieras información más detallada, pero como estás empezando en estos temas, a lo mejor te podría confundir. Con los campos de bits es más complicado y depende del compilador y la arquitectura, pero tampoco es totalmente arbitrario. De nuevo, sin entrar en detalles, baste decir que tiene sentido la manera en que los está organizando y ordenando el compilador en este caso y que, aunque acostumbramos leer de izquierda a derecha, en realidad los dígitos se enumeran de derecha a izquierda. En los últimos ejemplos, el problema principal es, como ya te dijeron, el hecho de usar union. Y esto no sólo aplica a las estructuras, sino que, en general, se supone que sólo uno de sus miembros es válido en un momento dado: el último que hayas modificado. Es decir, si haces esto: vec.producto.codigo = 5;
lo más correcto sería que, a partir de ahí, consideres a vec.empaquetado como inactivo o "inválido" y no accedas a él. Si después modificaras el otro miembro: vec.empaquetado = loquesea;
ahora este miembro es el activo y vec.producto se inactiva, y así sucesivamente. De hecho, en C++ es incorrecto acceder al miembro "inactivo" (aunque compila y en general funciona, técnicamente no es válido). En C se permite y a veces se hace, pero no se recomienda, pues puede dar pie a errores. Finalmente, para ver la representación interna de una estructura, es mejor usar punteros a unsigned char, no unions, y menos mediante un miembro entero, pues ¿qué tipo de dato debería tener empaquetado, si no sabemos a priori cuál es el tamaño de la estructura producto_t? Eso es parte del problema que se está dando, ya que en el ejemplo la estructura ocupa 6 bytes, pero empaquetado es unsigned short int (2 bytes) o unsigned int (4 bytes). No es que procedencia y basura estén escondidos o perdidos; están ahí, pero empaquetado no tiene el tamaño suficiente para mostrarlos. Se podría cambiar a un tipo de dato de 64 bits (aunque ya no se podría usar el especificador %X para imprimirlos sino que habría que recurrir a la macro PRIX64), pero de nuevo, esta no es una manera aconsejable de usar union y estamos adivinando qué tamaño tendrá la estructura. Lo mejor, reitero, sería usar un unsigned char* que apunte a la dirección de vec.producto, y vaya recorriendo por sus bytes y mostrándolos. O en todo caso, si no quieres usar punteros, puedes hacer que empaquetado sea arreglo de caracteres como lo hacías originalmente: unsigned char empaquetado[sizeof(s_producto)];
y vas iterando, desde 0 hasta sizeof(s_producto) - 1: printf("%02X ", vec. empaquetado[i ]);
aunque, dado que la arquitectura x86 (y x86-64) es little-endian (es decir, se almacena primero el byte menos significativo, o más a la derecha), los datos te aparecerían de manera inversa a la que probablemente esperarías, por lo que tal vez sea mejor mostrarlos al revés, iterando desde sizeof(s_producto) -1 hasta 0.
|
|
« Última modificación: 29 Septiembre 2023, 05:04 am por RayR »
|
En línea
|
|
|
|
MAFUS
Desconectado
Mensajes: 1.603
|
Motivado por la respuesta de RayR decidí encontrar los bits que había perdido. Aquí están #include <stdio.h> typedef struct { unsigned char codigo : 4; unsigned short cantidad: 9; unsigned char procedencia: 1; unsigned char basura: 2; } producto_t; int main() { unsigned base[2] = { 0 }; producto_t *prod = (producto_t *) &base; printf("tamanno del mapa de bits: %lu bytes\n\n", sizeof(producto_t )); prod->codigo = 0xF; printf("codigo : %08x %08x\n", base [1], base [0]); prod->procedencia = 0x1; printf("procedencia: %08x %08x\n", base [1], base [0]); prod->cantidad = 0X1FF; printf("cantidad : %08x %08x\n", base [1], base [0]); prod->basura = 3; printf("basura : %08x %08x\n", base [1], base [0]); }
Y la respuesta es: tamanno del mapa de bits: 6 bytes
codigo : 00000000 0000000f procedencia: 00000001 0000000f cantidad : 00000001 01ff000f basura : 00000007 01ff000f
|
|
« Última modificación: 29 Septiembre 2023, 12:25 pm por MAFUS »
|
En línea
|
|
|
|
|
Mensajes similares |
|
Asunto |
Iniciado por |
Respuestas |
Vistas |
Último mensaje |
|
|
Tamaño de variables y campos de bits
PHP
|
morpheus747
|
1
|
4,770
|
12 Enero 2009, 04:11 am
por Karman
|
|
|
Memoria en campos de bits
Programación C/C++
|
Shon
|
3
|
3,951
|
31 Julio 2010, 21:01 pm
por do-while
|
|
|
tengo una notbook de 64 bits y necesito trabajar con un prog de 32 bits
Programación Visual Basic
|
twister69
|
2
|
3,320
|
4 Septiembre 2012, 13:55 pm
por twister69
|
|
|
[Ubuntu] Instalar librerías 32 bits para SO a 64 bits (ia32-libs)
GNU/Linux
|
#!Mitsu
|
2
|
17,119
|
29 Noviembre 2014, 23:43 pm
por #!Mitsu
|
|
|
campos de bits + punteros + macros
Programación C/C++
|
michael_753
|
1
|
2,567
|
24 Octubre 2023, 14:12 pm
por MAFUS
|
|