Buscar..


Introducción

De forma predeterminada, los compiladores de C diseñan estructuras para que se pueda acceder rápidamente a cada miembro, sin incurrir en penalizaciones para el acceso no alineado, un problema con máquinas RISC como el DEC Alpha y algunas CPU ARM.

Dependiendo de la arquitectura de la CPU y el compilador, una estructura puede ocupar más espacio en la memoria que la suma de los tamaños de sus miembros componentes. El compilador puede agregar relleno entre los miembros o al final de la estructura, pero no al principio.

El embalaje anula el relleno predeterminado.

Observaciones

Eric Raymond tiene un artículo en The Lost Art of C Structure Packing que es útil para leer.

Estructuras de embalaje

Por defecto, las estructuras se rellenan en C. Si desea evitar este comportamiento, debe solicitarlo explícitamente. Bajo GCC es __attribute__((__packed__)) . Considere este ejemplo en una máquina de 64 bits:

struct foo {
    char *p;  /* 8 bytes */
    char c;   /* 1 byte  */
    long x;   /* 8 bytes */
};

La estructura se rellenará automáticamente para tener 8-byte alineación de 8-byte y se verá así:

struct foo {
    char *p;     /* 8 bytes */
    char c;      /* 1 byte  */

    char pad[7]; /* 7 bytes added by compiler */

    long x;      /* 8 bytes */
};

Entonces sizeof(struct foo) nos dará 24 lugar de 17 . Esto sucedió debido a un compilador de 64 bits de lectura / escritura de / a Memoria en 8 bytes de palabra en cada paso y es obvio cuando se intenta escribir el char c; un byte en la memoria, un total de 8 bytes (es decir, palabra) recuperado y consume solo el primer byte de la misma, y ​​sus siete bytes sucesivos permanecen vacíos y no son accesibles para ninguna operación de lectura y escritura para el relleno de la estructura.

Estructura de embalaje

Pero si agrega el atributo packed , el compilador no agregará relleno:

struct __attribute__((__packed__)) foo {
    char *p;  /* 8 bytes */
    char c;   /* 1 byte  */
    long x;   /* 8 bytes */
};

Ahora sizeof(struct foo) devolverá 17 .

Generalmente se utilizan estructuras compactas:

  • Para ahorrar espacio.
  • Para formatear una estructura de datos para transmitir a través de la red sin depender de cada alineación de la arquitectura de cada nodo de la red.

Se debe tener en cuenta que algunos procesadores como el ARM Cortex-M0 no permiten el acceso a la memoria no alineada; en tales casos, el empaquetado de la estructura puede llevar a un comportamiento indefinido y puede bloquear la CPU.

Acolchado de estructura

Supongamos que esta struct está definida y compilada con un compilador de 32 bits:

struct test_32 {
    int a;      // 4 byte
    short b;    // 2 byte
    int c;      // 4 byte    
} str_32;

Podríamos esperar que esta struct ocupe solo 10 bytes de memoria, pero al imprimir sizeof(str_32) vemos que usa 12 bytes.

Esto sucedió porque el compilador alinea las variables para un acceso rápido. Un patrón común es que cuando el tipo de base ocupa N bytes (donde N es una potencia de 2 como 1, 2, 4, 8, 16 y rara vez más grande), la variable debe alinearse en un límite de N bytes ( un múltiplo de N bytes).

Para la estructura mostrada con sizeof(int) == 4 y sizeof(short) == 2 , un diseño común es:

  • int a; almacenado en offset 0; talla 4.
  • short b; almacenado en el desplazamiento 4; talla 2
  • relleno sin nombre en el desplazamiento 6; talla 2
  • int c; almacenado en el desplazamiento 8; talla 4.

Así, struct test_32 ocupa 12 bytes de memoria. En este ejemplo, no hay relleno final.

El compilador se asegurará de que cualquier variable struct test_32 se almacene comenzando en un límite de 4 bytes, de modo que los miembros dentro de la estructura se alinearán correctamente para un acceso rápido. Se requieren funciones de asignación de memoria como malloc() , calloc() y realloc() para garantizar que el puntero devuelto esté lo suficientemente bien alineado para su uso con cualquier tipo de datos, por lo que las estructuras asignadas dinámicamente también se alinearán correctamente.

Puede terminar con situaciones extrañas, como en un procesador Intel x86_64 de 64 bits (por ejemplo, Intel Core i7 - una Mac que ejecuta macOS Sierra o Mac OS X), donde al compilar en modo de 32 bits, los compiladores colocan double alineación en un Límite de 4 bytes; pero, en el mismo hardware, al compilar en modo de 64 bits, los compiladores colocan double alineación en un límite de 8 bytes.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow