¿Qué es una estructura “empaquetada” en C?

5 minutos de lectura

avatar de usuario
PICtucerebro

Estoy revisando un código C escrito para el compilador Microchip C30 y, a menudo, veo estructuras definidas de la siguiente manera:

typedef struct __attribute__((__packed__)) 
{
    IP_ADDR     MyIPAddr;               // IP address
    IP_ADDR     MyMask;                 // Subnet mask
    IP_ADDR     MyGateway;              // Default Gateway
        // etc...
} APP_CONFIG;

¿Qué significa empacado?

avatar de usuario
Juliano

Cuando se definen las estructuras, el compilador puede agregar rellenos (espacios sin datos reales) para que los miembros caigan en los límites de direcciones que son más fáciles de acceder para la CPU.

Por ejemplo, en una CPU de 32 bits, los miembros de 32 bits deben comenzar en direcciones que sean múltiplos de 4 bytes para poder acceder a ellos de manera eficiente (lectura y escritura). La siguiente definición de estructura agrega un relleno de 16 bits entre ambos miembros, de modo que el segundo miembro cae en un límite de dirección adecuado:

struct S {
    int16_t member1;
    int32_t member2;
};

La estructura en memoria de la estructura anterior en una arquitectura de 32 bits es (~ = relleno):

+---------+---------+
| m1 |~~~~|   m2    |
+---------+---------+

Cuando se empaqueta una estructura, estos rellenos no se insertan. El compilador tiene que generar más código (que se ejecuta más lento) para extraer los miembros de datos no alineados y también para escribir en ellos.

La misma estructura, cuando esté empaquetada, aparecerá en la memoria como algo así como:

+---------+---------+
| m1 |   m2    |~~~~
+---------+---------+

  • Pregunta tangencial, pero ¿por qué uno crearía una estructura empaquetada si hace que las cosas sean más lentas? ¿Es para reducir la huella de memoria?

    – maldito

    12 de diciembre de 2017 a las 6:05

  • Un caso de uso: se usan muy a menudo para definir e implementar protocolos de red, formatos binarios… Por lo general, no desea agregar rellenos a las estructuras que se almacenarán o enviarán a través de la red.

    – jjmontes

    4 de abril de 2018 a las 14:25

  • Teniendo en cuenta su ejemplo, 16 bits + 32 bits = 48 bits = 12 bytes y eso es, de hecho, un múltiplo de 4 bytes. ¿Por qué un compilador aplicaría entonces relleno?

    – Maxitj

    23 de octubre de 2018 a las 11:19


  • @Maxitj, 48 bits son 6 bytes, no 12.

    – Juliano

    23/10/2018 a las 11:50

  • @Juliano tonto, estaba muy cansado mientras leía esta publicación 🙂

    – Maxitj

    29/10/2018 a las 18:25

avatar de usuario
NPE

Indica al compilador que no agregue ningún relleno entre los miembros del struct.

Véase, por ejemplo, esta página.

  • Supongo que quiere decir lo contrario: empaquetado significa omitir cualquier relleno y no agregarlo.

    – flolo

    29 de marzo de 2011 a las 13:26

  • El enlace en esta respuesta está roto, ¿alguna posibilidad de arreglarlo?

    – Kev

    20 de noviembre de 2011 a las 16:41

  • Detalle: “no agregar ningún relleno entre los miembros” es más como agregar mínimo embalaje. Ciertas arquitecturas aún pueden requerir algo de relleno en casos seleccionados. (p.ej int debe estar en un límite de dirección uniforme para evitar fallas en el bus).

    – chux – Reincorporar a Monica

    12/07/2016 a las 17:37

avatar de usuario
Babaján

Permítanme explicar el concepto de relleno en estructuras y luego estructuras empaquetadas tomando un ejemplo.

Y luego veamos por qué se requiere embalaje.

Relleno:

struct eg_struct
{
           unsigned char abc;
           unsigned int  xyz;
}

Cuando la estructura se declara como arriba en una arquitectura de 16 bits, la variable abc se le asignaría alguna dirección. La siguiente dirección no está asignada a la variable. xyzen su lugar, se agrega un byte adicional, y luego la siguiente dirección se asignaría a la variable xyz.

Al final, la estructura se parece a la siguiente:

struct eg_struct
{
           unsigned char abc;
           unsigned char paddedbytes[1];
           unsigned int  xyz;
}

El relleno hace que las direcciones de las variables miembro sean fácilmente accesibles para el microcontrolador. La desventaja son los bytes adicionales innecesarios que entran en escena.

Embalaje:

Si se declara la misma estructura usando el atributo “packed”, el byte extra no se agregará después de la variable abc.

Permítanme dar un ejemplo donde se necesita embalaje:

Considere un microcontrolador interconectado con una EEPROM donde se almacena alguna estructura.

Imagine que una función que escribe en la EEPROM se vería de la siguiente manera:

Write_EEPROM(EEPROM address, Ram address, Byte count);

Ahora bien, si no se hace el empaquetado, los bytes extra rellenos ocuparían espacio en la EEPROM, lo cual no sirve de nada.

  • @laurenz albe Gracias por hacer mi respuesta más presentable.

    – Babaján

    28 de abril de 2019 a las 18:31


_attribute__((__packed__)) significa (muy probablemente) “no inserte ningún relleno para hacer las cosas más rápido” y también puede significar “no inserte ninguna alineación para preservar la alineación”.

Una cosa que no se ha mencionado explícitamente es que el empaquetado generalmente se realiza para que coincida con las estructuras de campo predefinidas. Por ejemplo, en la capa de bajo nivel de una interfaz de red, se intercambia una serie de bytes entre máquinas en red. Una vez recibidos los datos, será necesario asignarlos a una estructura de alto nivel para que los datos puedan manipularse fácilmente. Esto es cuando normalmente es necesario el no relleno, de modo que la estructura se asigna directamente a los bytes.

El intercambio de datos de red también implica un problema de endianness de bytes (es decir, casi todos los datos de red utilizan el formato big endian independientemente del endianness de las máquinas de origen y destino).

Además, algunas máquinas no poder acceder a datos amplios en direcciones no alineadas, por ejemplo, los núcleos Cortex-M0 no pueden acceder a datos de 32 bits en direcciones no alineadas de 32 bits, por lo que se debe tener cuidado al escribir código de red en tales casos.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad