Categories: NoticiasTecnología

Unión en lenguaje C para empaquetar y desempacar datos

Obtenga información sobre cómo empaquetar y desempacar datos con uniones en lenguaje C.

En un artículo anterior, discutimos que la aplicación original de uniones había estado creando un área de memoria compartida para variables mutuamente excluyentes. Sin embargo, a lo largo del tiempo, los programadores han utilizado uniones ampliamente para una aplicación completamente diferente: extraer partes más pequeñas de datos de un objeto de datos más grande. En este artículo, veremos con mayor detalle esta aplicación particular de los sindicatos.

Usando Uniones para Empaquetar / Desempacar Datos

Los miembros de una unión se almacenan en un área de memoria compartida. Esta es la característica clave que nos permite encontrar aplicaciones interesantes para los sindicatos.

Considere la siguiente unión:

                    Unión {
      uint16_t palabra;
      estructura {
              uint8_t byte1;
              uint8_t byte2;
              };
} u1;
                  

Hay dos miembros dentro de esta unión: el primer miembro, "palabra", es una variable de dos bytes. El segundo miembro es una estructura de dos variables de un byte. Los dos bytes asignados para la unión se comparten entre sus dos miembros.

El espacio de memoria asignado puede ser como se muestra en la Figura 1 a continuación.

Figura 1

Mientras que la variable "palabra" se refiere a todo el espacio de memoria asignado, las variables "byte1" y "byte2" se refieren a las áreas de un byte que construyen la variable "palabra". ¿Cómo podemos usar esta característica? Suponga que tiene dos variables de un byte, "x" y "y", que deben combinarse para producir una sola variable de dos bytes.

En este caso, puede usar la unión anterior y asignar "x" y "y" a los miembros de la estructura de la siguiente manera:

                    u1.byte1 = y;
u1.byte2 = x;
                  

Ahora, podemos leer la "palabra" miembro de la unión para obtener una variable de dos bytes compuesta de "x" y "y" variables (consulte la Figura 2).

Figura 2

El ejemplo anterior muestra el uso de uniones para empaquetar dos variables de un byte en una sola variable de dos bytes. También podríamos hacer lo contrario: escriba un valor de dos bytes en "palabra" y descomprímalo en dos variables de un byte leyendo las variables "x" y "y". Escribir un valor para un miembro de un sindicato y leer a otro miembro de la misma a veces se conoce como "imputación de datos".

El procesador endianness

Al utilizar uniones para empaquetar / desempaquetar datos, debemos tener cuidado con el endianness del procesador. Como se analiza en el artículo de Robert Keim sobre la endianness, este término especifica el orden en que se almacenan en la memoria los bytes de un objeto de datos. Un procesador puede ser little endian o big endian. Con un procesador big-endian, los datos se almacenan de manera que el byte que contiene el bit más significativo tenga la dirección de memoria más baja. En los sistemas little-endian, el byte que contiene el bit menos significativo se almacena primero.

El ejemplo representado en la Figura 3 ilustra el almacenamiento de little endian y big endian de la secuencia 0x01020304.

Figura 3. Imagen cortesía de IAR.

Usemos el siguiente código para experimentar con la unión de la sección anterior:

                    #include & ltstdio.h & gt
#include & ltstdint.h & gt

int main ()
{
  
Unión {
  estructura {
    uint8_t byte1;
    uint8_t byte2;
  };
  uint16_t palabra;
    
} u1;
  
  
u1.byte1 = 0x21;
u1.byte2 = 0x43;
  
printf ("Word es:% # X", u1.word);
devuelve 0;
}
                  

Ejecutando este código, obtengo el siguiente resultado:

Palabra es: 0X4321

Esto muestra que el primer byte del espacio de memoria compartido ("u1.byte1") se utiliza para almacenar el byte menos significativo (0X21) de la variable "word". En otras palabras, el procesador que estoy usando para ejecutar el código es little endian.

Como puede ver, esta aplicación particular de uniones puede exhibir un comportamiento dependiente de la implementación. Sin embargo, esto no debería ser un problema grave porque, para tal codificación de bajo nivel, generalmente conocemos la endianidad del procesador. En caso de que no sepamos estos detalles, podemos usar el código anterior para averiguar cómo se organizan los datos en la memoria.

Solución alternativa

En lugar de utilizar uniones, también podemos utilizar los operadores bitwise para realizar el empaquetado o desempaquetado de datos. Por ejemplo, podemos usar el siguiente código para combinar dos variables de un byte, "byte3" y "byte4", y generar una sola variable de dos bytes ("word2"):

                    word2 = (((uint16_t) byte3) 
                  

Comparemos la salida de estas dos soluciones en los casos de little endian y big endian. Considere el siguiente código:

                    #include & ltstdio.h & gt
#include & ltstdint.h & gt

int main ()
{
Unión {
      estructura {
              uint8_t byte1;
              uint8_t byte2;
             };
      uint16_t word1;
      } u1;
  
u1.byte1 = 0x21;
u1.byte2 = 0x43;
printf ("Word1 es:% # X  n", u1.word1);
  
uint8_t byte3, byte4;
uint16_t word2;
byte3 = 0x21;
byte4 = 0x43;
word2 = (((uint16_t) byte3) 
                  

Si compilamos este código para un procesador big endian como TMS470MF03107, la salida será:

Word1 es: 0X2143

Word2 es: 0X2143

Sin embargo, si lo compilamos para un pequeño procesador endian como STM32F407IE, la salida será:

Word1 es: 0X4321

Word2 es: 0X2143

Si bien el método basado en la unión exhibe un comportamiento dependiente del hardware, el método basado en la operación de cambio conduce al mismo resultado independientemente de la capacidad del procesador. Esto se debe al hecho de que, con este último enfoque, estamos asignando un valor al nombre de una variable ("word2") y el compilador se encarga de la organización de memoria empleada por el dispositivo. Sin embargo, con el método basado en la unión, estamos cambiando el valor de los bytes que construyen la variable "word1".

Aunque el método basado en la unión muestra un comportamiento dependiente del hardware, tiene la ventaja de ser más legible y más fácil de mantener. Es por eso que muchos programadores prefieren usar uniones para esta aplicación.

Un ejemplo práctico de "Data Punning"

Al trabajar con protocolos comunes de comunicación en serie, es posible que debamos realizar el empaquetado o desempaquetado de datos. Considere un protocolo de comunicación en serie que envía / recibe un byte de datos durante cada secuencia de comunicación. Mientras trabajemos con variables de un byte, es fácil transferir los datos, pero ¿qué sucede si tenemos una estructura de tamaño arbitrario que debería pasar por el enlace de comunicación? En este caso, tenemos que representar de alguna manera nuestro objeto de datos como una matriz de variables largas de un byte. Una vez que obtenemos esta representación de matriz de bytes, podemos transferir los bytes a través del enlace de comunicación. Luego, en el extremo del receptor, podemos empaquetarlos adecuadamente y reconstruir la estructura original.

Por ejemplo, supongamos que necesitamos enviar una variable flotante, "f1", a través de la comunicación UART. Una variable flotante usualmente ocupa cuatro bytes. Por lo tanto, podemos usar la siguiente unión como un búfer para extraer los cuatro bytes de "f1":

                    Unión {
      flotar f;
      estructura {
             uint8_t byte[4];
             };
} u1;
                  

El transmisor escribe la variable "f1" en el miembro flotante de la unión. Luego, lee la matriz de "bytes" y envía los bytes por el enlace de comunicación. El receptor hace lo contrario: escribe los datos recibidos en la matriz "byte" de su propia unión y lee la variable flotante de la unión como el valor recibido. Podríamos hacer esta técnica para transferir un objeto de datos de tamaño arbitrario. El siguiente código puede ser una prueba simple para verificar esta técnica.

                    #include & ltstdio.h & gt
#include & ltstdint.h & gt

int main ()
{

flotador f1 = 5,5;
  
union buffer {
             flotar f;
             estructura {
                    uint8_t byte[4];
                    };
             };
  
union buffer buff_Tx;
union buffer buff_Rx;
buff_Tx.f = f1;
buff_Rx.byte[0] = buff_Tx.byte[0];
buff_Rx.byte[1] = buff_Tx.byte[1];
buff_Rx.byte[2] = buff_Tx.byte[2];
buff_Rx.byte[3] = buff_Tx.byte[3];
  
printf ("Los datos recibidos son:% f", buff_Rx.f);
  
devuelve 0;
}
                  

La figura 4 a continuación visualiza la técnica discutida. Tenga en cuenta que los bytes se transfieren secuencialmente.

Figura 4

Conclusión

Mientras que la aplicación original de uniones creaba un área de memoria compartida para variables mutuamente excluyentes, a lo largo del tiempo, los programadores han usado uniones ampliamente para una aplicación completamente diferente: usar uniones para el empaquetado / desempaquetado de datos. Esta aplicación particular de los sindicatos implica escribir un valor para un miembro del sindicato y leer a otro miembro del mismo.

La "manipulación de datos" o el uso de uniones para el empaquetado / desempaquetado de datos puede generar un comportamiento dependiente del hardware. Sin embargo, tiene la ventaja de ser más legible y fácil de mantener. Es por eso que muchos programadores prefieren usar uniones para esta aplicación. La "manipulación de datos" puede ser particularmente útil cuando tenemos un objeto de datos de tamaño arbitrario que debe pasar por un enlace de comunicación en serie.

Para ver una lista completa de mis artículos, visite esta página.

ga('create', 'UA-1454132-1', 'auto'); ga('require', 'GTM-MMWSMVL'); ga('require', 'displayfeatures'); ga('set',{'dimension1':'computing,memory,computers-peripherals'}); ga('set',{'contentGroup1':'computing,memory,computers-peripherals'});

ga('set',{'dimension3':"June 05, 2019"});

ga('set',{'dimension4':"Steve Arar"});

ga('send', 'pageview');

!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod? n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n; n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0; t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window, document,'script','https://connect.facebook.net/en_US/fbevents.js'); fbq('init', '1808435332737507'); // Insert your pixel ID here. fbq('track', 'PageView'); fbq('track', 'ViewContent', { content_ids: ['computing','memory','computers-peripherals'], content_type: 'category'});

_linkedin_data_partner_id = "353081"; (function(){var s = document.getElementsByTagName("script")[0]; var b = document.createElement("script"); b.type = "text/javascript";b.async = true; b.src = "https://snap.licdn.com/li.lms-analytics/insight.min.js"; s.parentNode.insertBefore(b, s);})(); } if(jstz.determine().name().indexOf("Europe") === -1) { showSocialCode(); // NOT EU } else { showSocialCode(); window.addEventListener("load", function () { window.cookieconsent.initialise({ "palette": { "popup": { "background": "#252e39" }, "button": { "background": "#14a7d0" } }, "type": "opt-out", "content": { "message": "This website uses tracking cookies to ensure you get the best experience on our website.", "href": "https://www.allaboutcircuits.com/privacy-policy/", "dismiss": "OK, GOT IT" }, onInitialise: function (status) { var type = this.options.type; var didConsent = this.hasConsented(); if (type == 'opt-out' && didConsent) { console.log("eu"); //showSocialCode(); } },

onStatusChange: function (status, chosenBefore) { var type = this.options.type; var didConsent = this.hasConsented(); if (type == 'opt-out' && didConsent) { console.log("eu"); //showSocialCode(); } },

onRevokeChoice: function () { var type = this.options.type; if (type == 'opt-out') { console.log("eu"); //showSocialCode(); } },

}) }); }

Maria Montero

Me apasiona la fotografía y la tecnología que nos permite hacer todo lo que siempre soñamos. Soñadora y luchadora. Actualmente residiendo en Madrid.

Recent Posts

Máquina de mano Lean, Green, Raspberry Pi

Los días felices de la PDA y Blackberry han quedado definitivamente atrás, pero el factor…

2 years ago

Cómo pronosticar series de tiempo usando autorregresión

Tutorial sobre cómo pronosticar usando un modelo autorregresivo en PythonFoto de Aron Visuals en UnsplashForecasting…

2 years ago

Aquí están todas las formas en que puede cargar su AirPods Pro

Si tienes un iPhone, los AirPods Pro son la opción obvia para escuchar música, ¡aunque…

2 years ago

Las principales noticias tecnológicas del lunes: la prohibición de clientes de terceros de Twitter parece no ser un accidente

Ilustración de Alex Castro / The Verge Plus nuevos rumores sobre el quinto Galaxy Fold.…

2 years ago

AirPods Max 2: aquí están las características más solicitadas

Se rumorea que los auriculares premium de próxima generación de Apple, los AirPods Max 2,…

2 years ago

El remake de Dead Space continúa luciendo terriblemente genial en el nuevo tráiler de la historia

El desarrollador Motive Studio y el editor EA han lanzado un nuevo tráiler de la…

2 years ago