Acolchado y embalaje de la estructura

8 minutos de lectura

avatar de usuario
manú

Considerar:

struct mystruct_A
{
   char a;
   int b;
   char c;
} x;

struct mystruct_B
{
   int b;
   char a;
} y;

Los tamaños de las estructuras son 12 y 8 respectivamente.

¿Estas estructuras están acolchadas o empacadas?

¿Cuándo se lleva a cabo el acolchado o el embalaje?

  • Leer stackoverflow.com/questions/119123/…

    – Prasoon Saurav

    29 de noviembre de 2010 a las 17:18

  • El arte perdido del empaque de estructura C – catb.org/esr/estructura-embalaje

    – Pablo

    06/04/2016 a las 10:34

  • padding hace las cosas más grandes. packing hace las cosas más pequeñas. Totalmente diferente.

    – smwikipedia

    25 de junio de 2017 a las 2:45

  • @Paolo, ese enlace de Lost Art no muestra lo que sucede cuando hay una alineación de puntero y lo anterior donde dos entradas pueden ser una tras otra.

    – mlestudiante33

    10 de julio de 2020 a las 19:37

  • Relacionado, para C++: stackoverflow.com/questions/44287060/…

    – Gabriel grapas

    17 sep 2020 a las 16:20

avatar de usuario
Nikolái Fetissov

Relleno alinea miembros de la estructura a los límites de direcciones “naturales”, por ejemplo, int los miembros tendrían compensaciones, que son mod(4) == 0 en plataforma de 32 bits. El relleno está activado de forma predeterminada. Inserta los siguientes “huecos” en su primera estructura:

struct mystruct_A {
    char a;
    char gap_0[3]; /* inserted by compiler: for alignment of b */
    int b;
    char c;
    char gap_1[3]; /* -"-: for alignment of the whole struct in an array */
} x;

Embalajepor otro lado, evita que el compilador haga relleno, esto debe solicitarse explícitamente, bajo GCC es __attribute__((__packed__))entonces lo siguiente:

struct __attribute__((__packed__)) mystruct_A {
    char a;
    int b;
    char c;
};

produciría una estructura de tamaño 6 en una arquitectura de 32 bits.

Sin embargo, una nota: el acceso a la memoria no alineado es más lento en las arquitecturas que lo permiten (como x86 y amd64), y está explícitamente prohibido en arquitecturas de alineación estricta como SPARC.

  • Me pregunto: ¿la prohibición de la memoria no alineada en la chispa significa que no puede manejar matrices de bytes habituales? El empaque de estructuras, como sé, se usa principalmente para transmitir (es decir, redes) datos, cuando necesita convertir una matriz de bytes en una estructura, y asegurarse de que una matriz se ajuste a los campos de una estructura. Si la chispa no puede hacer eso, ¿cómo funcionan esos?

    – Hola angel

    24 de mayo de 2014 a las 9:19

  • Es exactamente por eso que, si observa los diseños de encabezado de IP, UDP y TCP, verá que todos los campos enteros están alineados.

    – Nikolái Fetissov

    24 mayo 2014 a las 15:09

  • El “Arte perdido del empaque de estructura C” explica las optimizaciones de relleno y empaque: catb.org/esr/estructura-embalaje

    – Rob11311

    29 de junio de 2014 a las 16:45

  • ¿El primer miembro tiene que ser lo primero? Pensé que el arreglo depende totalmente de la implementación y no se puede confiar en él (incluso de una versión a otra).

    – todotucódigo

    30 de julio de 2015 a las 1:23

  • +allyyourcode El estándar garantiza que se conservará el orden de los miembros y que el primer miembro comenzará con un desplazamiento de 0.

    – martinkunev

    8 de diciembre de 2017 a las 14:32

  • Esto no explica el empaquetamiento de estructuras, pero ilustra bastante bien la alineación de palabras de la CPU.

    – David Foerster

    7 ago 2016 a las 23:49

  • ¿Dibujaste eso con pintura? 🙂

    – Ciro Santilli Путлер Капут 六四事

    30 de julio de 2017 a las 10:58

  • @CiroSantilli709大抓捕六四事件法轮功, estaba en gimp, pero creo que me habría ahorrado algo de tiempo haciéndolo en pintura jaja

    – IanC

    30 de julio de 2017 a las 22:21

  • Aún mejor desde el código abierto (Y)

    – Ciro Santilli Путлер Капут 六四事

    30 de julio de 2017 a las 22:25

  • También es muy conveniente al leer estructuras de archivos. Uno puede simplemente leer en un búfer y luego memcpy etc. directamente en una estructura.

    – usuario3342816

    19 de julio de 2021 a las 3:15

avatar de usuario
rossjb

Las variables se almacenan en cualquier dirección divisible por su alineación (generalmente por su tamaño). Por lo tanto, el relleno/empaquetado no es solo para la estructura. Realmente, todos los datos tienen su propio requisito de alineación:

int main(void) {
    // We assume the `c` is stored as first byte of machine word
    // as a convenience! If the `c` was stored as a last byte of previous
    // word, there is no need to pad bytes before variable `i`
    // because `i` is automatically aligned in a new word.

    char      c;  // starts from any addresses divisible by 1(any addresses).
    char pad[3];  // not-used memory for `i` to start from its address.
    int32_t   i;  // starts from any addresses divisible by 4.

Esto es similar a struct, pero hay algunas diferencias. En primer lugar, podemos decir que hay dos tipos de relleno: a) Para iniciar correctamente cada miembro desde su dirección, se insertan algunos bytes entre los miembros. b) Para iniciar correctamente la siguiente instancia de estructura desde su dirección, se agregan algunos bytes a cada estructura:

// Example for rule 1 below.
struct st {
    char      c;  // starts from any addresses divisible by 4, not 1.
    char pad[3];  // not-used memory for `i` to start from its address.
    int32_t   i;  // starts from any addresses divisible by 4.
};

// Example for rule 2 below.
struct st {
    int32_t   i;  // starts from any addresses divisible by 4.
    char      c;  // starts from any addresses.
    char pad[3];  // not-used memory for next `st`(or anything that has same
                  // alignment requirement) to start from its own address.
};
  1. El primer miembro de la estructura siempre comienza desde cualquier dirección divisible por el propio requisito de alineación de la estructura, que está determinado por el requisito de alineación del miembro más grande (aquí 4alineación de int32_t). Esto es diferente con las variables normales. Las variables normales pueden iniciar cualquier dirección divisible por su alineación, pero no es el caso del primer miembro de struct. Como sabes, la dirección de una estructura es la misma que la dirección de su primer miembro.
  2. Puede haber bytes finales rellenados adicionales dentro de una estructura, haciendo que la siguiente estructura (o el siguiente elemento en una matriz de estructuras) comience desde su propia dirección. Pensar en struct st arr[2];. Para hacer arr[1](arr[1]primer miembro de) a partir de una dirección divisible por 4, debemos agregar 3 bytes al final de cada estructura.

Esto es lo que aprendí de El arte perdido del embalaje estructural.

NOTA: puede investigar cuál es el requisito de alineación del tipo de datos a través de _Alignof operador. Además, puede obtener el desplazamiento del miembro dentro de una estructura a través de offsetof macro.

avatar de usuario
paxdiablo

¿Estas estructuras están acolchadas o empacadas?

Están acolchados.

La única posibilidad de que inicialmente me viene a la mente, dónde se podrían empacar, es si char y int eran del mismo tamaño, por lo que el tamaño mínimo de la char/int/char la estructura no permitiría relleno, lo mismo para el int/char estructura.

Sin embargo, eso requeriría ambos sizeof(int) y sizeof(char) ser – estar cuatro (para obtener las tallas doce y ocho). Toda la teoría se desmorona ya que está garantizado por el estándar que sizeof(char) es siempre uno.

Fueron char y int el mismo ancho, los tamaños serían uno y uno, no cuatro y cuatro. Entonces, para obtener un tamaño de doce, tendría que haber relleno después del campo final.


¿Cuándo se lleva a cabo el acolchado o el embalaje?

Siempre que la implementación del compilador lo desee. Los compiladores son libres de insertar relleno entre campos y después del campo final (pero no antes del primer campo).

Esto generalmente se hace por rendimiento, ya que algunos tipos funcionan mejor cuando están alineados en límites específicos. Incluso hay algunas arquitecturas que se negarán a funcionar (es decir, fallarán) si intenta acceder a datos no alineados (sí, estoy viendo usted, BRAZO).

Por lo general, puede controlar el empaquetado/relleno (que en realidad son extremos opuestos del mismo espectro) con características específicas de la implementación, como #pragma pack. Incluso si tú no poder haga eso en su implementación específica, puede verificar su código en el momento de la compilación para asegurarse de que cumpla con sus requisitos (usando características estándar de C, no cosas específicas de la implementación).

Por ejemplo:

// C11 or better ...
#include <assert.h>
struct strA { char a; int  b; char c; } x;
struct strB { int  b; char a;         } y;
static_assert(sizeof(struct strA) == sizeof(char)*2 + sizeof(int), "No padding allowed");
static_assert(sizeof(struct strB) == sizeof(char)   + sizeof(int), "No padding allowed");

Algo como esto se negará a compilar si hay algún relleno en esas estructuras.

avatar de usuario
gruñido

¡No hay peros al respecto! Quien quiera captar el tema debe hacer los siguientes,

  • Examinar detenidamente El arte perdido del embalaje estructural escrito por Eric S. Raymond
  • Un vistazo al ejemplo de código de Eric
  • Por último, pero no menos importante, no olvide la siguiente regla sobre el relleno que una estructura está alineada con los requisitos de alineación del tipo más grande.

¿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