Uso de uniones en C para empaquetado y desempaquetado eficiente de datos
Uso de uniones en C para empaquetado y desempaquetado eficiente de datos
Las uniones proporcionan una forma potente y eficiente en memoria de reinterpretar los mismos bytes como diferentes tipos de datos. Aunque originalmente se diseñaron para memoria compartida entre variables mutuamente exclusivas, los programadores modernos de C a menudo usan uniones para empaquetado, desempaquetado y type punning —especialmente al manejar protocolos serie o comunicaciones de bajo nivel.
Cómo una unión comparte memoria
Todos los miembros de una unión comparten la misma región de memoria. El miembro más grande determina el tamaño de la unión, y cada miembro comienza en la misma dirección.
union { uint16_t word; struct { uint8_t byte1; uint8_t byte2; }; } u1;
Aquí, word ocupa dos bytes, mientras que la estructura anidada divide esos dos bytes en byte1 y byte2. La disposición puede visualizarse como:

Empaquetando dos bytes en una palabra de 16 bits
Para combinar dos valores de 8 bits x e y en una palabra de 16 bits, asígnelos a los miembros de la estructura y luego lea el miembro word:
u1.byte1 = y; u1.byte2 = x; uint16_t packed = u1.word;
Este "data punning" es directo pero depende del orden de bytes del procesador.
El endianness del procesador importa
Los sistemas little-endian almacenan el byte menos significativo primero, mientras que los big-endian almacenan el más significativo primero. Considere la secuencia 0x01020304:

Ejecutar una prueba simple en una máquina little-endian produce:
#include <stdio.h> #include <stdint.h> int main() { union { struct { uint8_t byte1; uint8_t byte2; }; uint16_t word; } u1; u1.byte1 = 0x21; u1.byte2 = 0x43; printf("Word is: %#X\n", u1.word); return 0; }
Salida: Word is: 0X4321 —confirmando almacenamiento little-endian. En un objetivo big-endian el mismo código produciría 0X2143.
Alternativa bitwise: empaquetado independiente del endianness
Los desplazamientos y máscaras proporcionan una forma portable de construir un valor de 16 bits:
uint16_t word2 = (((uint16_t)byte3) << 8) | ((uint16_t)byte4);
Comparando ambos métodos:
#include <stdio.h> #include <stdint.h> int main() { union { struct { uint8_t byte1; uint8_t byte2; }; uint16_t word1; } u1; u1.byte1 = 0x21; u1.byte2 = 0x43; printf("Word1 is: %#X\n", u1.word1); uint8_t byte3 = 0x21, byte4 = 0x43; uint16_t word2 = (((uint16_t)byte3) << 8) | ((uint16_t)byte4); printf("Word2 is: %#X\n", word2); return 0; }
En un procesador big-endian: Word1 is: 0X2143, Word2 is: 0X2143. En little-endian: Word1 is: 0X4321, Word2 is: 0X2143. El enfoque bitwise garantiza resultados consistentes entre arquitecturas.
Ejemplo práctico: Enviando un float por UART
Los enlaces serie típicamente transmiten un byte a la vez. Para enviar un float de 32 bits, podemos usar una unión para ver el float como un array de cuatro bytes:
union buffer { float f; struct { uint8_t byte[4]; }; };
Lado transmisor:
union buffer tx; union buffer rx; float f1 = 5.5f; tx.f = f1; for (int i = 0; i < 4; ++i) { // send tx.byte[i] over UART rx.byte[i] = tx.byte[i]; // Simulated receive } printf("The received data is: %f\n", rx.f);
Salida: The received data is: 5.500000. La unión separa limpiamente la representación en bytes del valor de punto flotante, haciendo el código legible y mantenible.

Conclusiones clave
- Las uniones permiten type punning, pero los resultados dependen del endianness.
- Los operadores bitwise proporcionan empaquetado portable e independiente de la arquitectura.
- Para claridad y mantenimiento, muchos desarrolladores aún prefieren uniones en contextos embebidos.
- Las uniones brillan al transmitir estructuras de datos arbitrarias sobre enlaces orientados a bytes.
Para explorar más sobre C, uniones y sistemas embebidos, explore mi lista completa de artículos.
Incrustado
- El Ejército de EE. UU. Avanza en Mantenimiento Basado en Condición con Sensores Innovadores y Analítica Predictiva
- Litmus y Oden Lanzan Soluciones IIoT Integradas para Fabricación Inteligente
- Visibilidad de la cadena de suministro: el papel crucial de los datos y el contexto
- Revolucionando la Gestión de Flotas: Integración de IA e IoT para Operaciones Más Inteligentes
- Semaforos en Nucleus RTOS: Servicios de utilidad y estructuras de datos
- CyrusOne mejora experiencia de usuario con monitorización de aplicaciones en tiempo real
- Cómo IIoT y Analítica de Datos Transforman EHS: Impulsando Seguridad, Eficiencia y Ahorros
- Construyendo IA responsable y confiable: Guía para explicabilidad, rendición de cuentas y ética
- Computación en la Niebla Explicada: Transformando el Flujo de Datos IoT y Reduciendo la Carga en la Nube
- AIoT industrial: Fusionando IA e IoT para impulsar la innovación de Industria 4.0
- Uniones en C explicadas: Gestión eficiente de memoria para múltiples tipos de datos