Relleno y embalaje de la estructura


Considere:

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 empaquetadas?

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

Author: gsamaras, 2010-11-29

7 answers

Relleno alinea los 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 una 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;

Empaquetar , por otro lado evita que el compilador haga relleno - esto tiene que ser solicitado explícitamente-bajo GCC es __attribute__((__packed__)), por lo que lo siguiente:

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

Produciría una estructura de tamaño 6 en un bit de 32 arquitectura.

Una nota sin embargo - el acceso a memoria no alineada es más lento en arquitecturas que lo permiten (como x86 y amd64), y está explícitamente prohibido en arquitecturas de alineación estricta como SPARC.

 211
Author: Nikolai Fetissov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-29 18:14:14

Sé que esta pregunta es antigua y la mayoría de las respuestas aquí explican el relleno muy bien, pero al tratar de entenderlo yo mismo pensé que tener una imagen "visual" de lo que está sucediendo ayudó.

El procesador lee la memoria en "trozos" de un tamaño definido (palabra). Digamos que la palabra del procesador tiene 8 bytes de largo. Verá la memoria como una gran fila de bloques de construcción de 8 bytes. Cada vez que necesite obtener alguna información de la memoria, alcanzará uno de esos bloques y obtendrá se.

Alineación de Variables

Como parece en la imagen de arriba, no importa dónde esté un Char (1 byte de largo), ya que estará dentro de uno de esos bloques, requiriendo que la CPU procese solo 1 palabra.

Cuando tratamos con datos más grandes que un byte, como un 4 byte int o un 8 byte double, la forma en que están alineados en la memoria hace una diferencia en cuántas palabras tendrán que ser procesadas por la CPU. Si los trozos de 4 bytes están alineados de una manera que siempre se ajustan al interior de un bloque (la dirección de memoria es un múltiplo de 4) solo una palabra tendrá que ser procesada. De lo contrario, un trozo de 4 bytes podría tener parte de sí mismo en un bloque y parte en otro, lo que requiere que el procesador procese 2 palabras para leer estos datos.

Lo mismo se aplica a un doble de 8 bytes, excepto que ahora debe estar en una dirección de memoria múltiple de 8 para garantizar que siempre estará dentro de un bloque.

Esto considera un procesador de textos de 8 bytes, pero el concepto se aplica a otros tamaños de palabras.

El relleno funciona por llenar los espacios entre esos datos para asegurarse de que estén alineados con esos bloques, mejorando así el rendimiento mientras lee la memoria.

Sin embargo, como se indica en otras respuestas, a veces el espacio importa más que el rendimiento en sí. Tal vez esté procesando muchos datos en una computadora que no tiene mucha RAM (se podría usar espacio de intercambio, pero es mucho más lento). Podría organizar las variables en el programa hasta que se haga el menor relleno (como se ejemplificó en gran medida en algunos otras respuestas) pero si eso no es suficiente, podría deshabilitar explícitamente el relleno, que es lo que es packing.

 28
Author: IanC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-08 00:25:57

El embalaje de la estructura suprime el relleno de la estructura, el relleno se usa cuando la alineación es más importante, el embalaje se usa cuando el espacio es más importante.

Algunos compiladores proporcionan #pragma para suprimir el relleno o para hacerlo empaquetar a n número de bytes. Algunos proporcionan palabras clave para hacer esto. Generalmente pragma que se utiliza para modificar el relleno de la estructura estará en el siguiente formato (depende del compilador):

#pragma pack(n)

Por ejemplo, ARM proporciona la palabra clave __packed para suprimir el relleno de estructura. Ir a través de su manual del compilador para aprender más sobre esto.

Así que una estructura empaquetada es una estructura sin relleno.

Generalmente se utilizarán estructuras empaquetadas

  • Para ahorrar espacio

  • Para formatear una estructura de datos para transmitir a través de la red utilizando algunos protocolo (esto no es una buena práctica, por supuesto, porque necesita
    deal with endianness)

 20
Author: user2083050,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-05 11:23:18

(Las respuestas anteriores explicaron la razón bastante clara, pero no parece totalmente clara sobre el tamaño del relleno, por lo que agregaré una respuesta de acuerdo con lo que aprendo de El Arte Perdido del Embalaje de Estructura C)


Reglas de alineación de memoria-para struct

Reglas:

  • Antes de cada miembro individual, habrá relleno para que comience en una dirección que sea divisible por su tamaño. por ejemplo, en un sistema de 64 bits, int debería comience en la dirección divisible por 4, y largo por 8, corto por 2.
  • char y char [] es especial, podría ser cualquier dirección de memoria, por lo que no necesitan relleno antes que ellos.
  • Para struct, aparte de la necesidad de alineación para cada miembro individual, el tamaño de toda la estructura en sí se alineará a un tamaño divisible por el tamaño del miembro individual más grande, por relleno al final. e. g si el miembro más grande de la estructura es largo entonces divisible por 8, int entonces por 4, corto entonces por 2.

Orden de los miembros:

  • El orden del miembro podría afectar el tamaño real de la estructura, así que tenga esto en cuenta. por ejemplo, stu_c y stu_d del ejemplo anterior tienen los mismos miembros, pero en diferente orden, y dan como resultado un tamaño diferente para la estructura.

Dirección en memoria-para struct

Reglas:

  • sistema de 32 bits
    Comienza desde (n * 8) bytes.
    Razón: el individuo más grande el miembro de la estructura es de 8 bytes.
  • sistema de 64 bits
    Comienza desde (n * 16) bytes.
    Reason: el miembro de estructura individual más grande es de 16 bytes.

Espacio Vacío:

  • El espacio vacío entre estructuras podría ser utilizado por variables no estructurales que podrían encajar.

Ejemplo

(para el sistema de 64 bits )

Memory_align.c :

/**
 * Memory align & padding - for struct.
 * compile: gcc memory_align.c
 * execute: ./a.out
 */ 
#include <stdio.h>

// size is 8, 4 + 1, then round to multiple of 4 (int's size),
struct stu_a {
    int i;
    char c;
};

// size is 16, 8 + 1, then round to multiple of 8 (long's size),
struct stu_b {
    long l;
    char c;
};

// size is 24, l need padding by 4 before it, then round to multiple of 8 (long's size),
struct stu_c {
    int i;
    long l;
    char c;
};

// size is 16, 8 + 4 + 1, then round to multiple of 8 (long's size),
struct stu_d {
    long l;
    int i;
    char c;
};

// size is 16, 8 + 4 + 1, then round to multiple of 8 (double's size),
struct stu_e {
    double d;
    int i;
    char c;
};

// size is 24, d need align to 8, then round to multiple of 8 (double's size),
struct stu_f {
    int i;
    double d;
    char c;
};

// size is 4,
struct stu_g {
    int i;
};

// size is 8,
struct stu_h {
    long l;
};

// test - padding within a single struct,
int test_struct_padding() {
    printf("%s: %ld\n", "stu_a", sizeof(struct stu_a));
    printf("%s: %ld\n", "stu_b", sizeof(struct stu_b));
    printf("%s: %ld\n", "stu_c", sizeof(struct stu_c));
    printf("%s: %ld\n", "stu_d", sizeof(struct stu_d));
    printf("%s: %ld\n", "stu_e", sizeof(struct stu_e));
    printf("%s: %ld\n", "stu_f", sizeof(struct stu_f));

    printf("%s: %ld\n", "stu_g", sizeof(struct stu_g));
    printf("%s: %ld\n", "stu_h", sizeof(struct stu_h));

    return 0;
}

// test - address of struct,
int test_struct_address() {
    printf("%s: %ld\n", "stu_g", sizeof(struct stu_g));
    printf("%s: %ld\n", "stu_h", sizeof(struct stu_h));
    printf("%s: %ld\n", "stu_f", sizeof(struct stu_f));

    struct stu_g g;
    struct stu_h h;
    struct stu_f f1;
    struct stu_f f2;
    int x = 1;
    long y = 1;

    printf("address of %s: %p\n", "g", &g);
    printf("address of %s: %p\n", "h", &h);
    printf("address of %s: %p\n", "f1", &f1);
    printf("address of %s: %p\n", "f2", &f2);
    printf("address of %s: %p\n", "x", &x);
    printf("address of %s: %p\n", "y", &y);

    // g is only 4 bytes itself, but distance to next struct is 16 bytes(on 64 bit system) or 8 bytes(on 32 bit system),
    printf("space between %s and %s: %ld\n", "g", "h", (long)(&h) - (long)(&g));

    // h is only 8 bytes itself, but distance to next struct is 16 bytes(on 64 bit system) or 8 bytes(on 32 bit system),
    printf("space between %s and %s: %ld\n", "h", "f1", (long)(&f1) - (long)(&h));

    // f1 is only 24 bytes itself, but distance to next struct is 32 bytes(on 64 bit system) or 24 bytes(on 32 bit system),
    printf("space between %s and %s: %ld\n", "f1", "f2", (long)(&f2) - (long)(&f1));

    // x is not a struct, and it reuse those empty space between struts, which exists due to padding, e.g between g & h,
    printf("space between %s and %s: %ld\n", "x", "f2", (long)(&x) - (long)(&f2));
    printf("space between %s and %s: %ld\n", "g", "x", (long)(&x) - (long)(&g));

    // y is not a struct, and it reuse those empty space between struts, which exists due to padding, e.g between h & f1,
    printf("space between %s and %s: %ld\n", "x", "y", (long)(&y) - (long)(&x));
    printf("space between %s and %s: %ld\n", "g", "y", (long)(&y) - (long)(&h));

    return 0;
}

int main(int argc, char * argv[]) {
    test_struct_padding();
    // test_struct_address();

    return 0;
}

Resultado de la ejecución - test_struct_padding():

stu_a: 8
stu_b: 16
stu_c: 24
stu_d: 16
stu_e: 16
stu_f: 24
stu_g: 4
stu_h: 8

Resultado de la Ejecución - test_struct_address():

stu_g: 4
stu_h: 8
stu_f: 24
address of g: 0x7fffd63a95d0  // struct variable - address dividable by 16,
address of h: 0x7fffd63a95e0  // struct variable - address dividable by 16,
address of f1: 0x7fffd63a95f0 // struct variable - address dividable by 16,
address of f2: 0x7fffd63a9610 // struct variable - address dividable by 16,
address of x: 0x7fffd63a95dc  // non-struct variable - resides within the empty space between struct variable g & h.
address of y: 0x7fffd63a95e8  // non-struct variable - resides within the empty space between struct variable h & f1.
space between g and h: 16
space between h and f1: 16
space between f1 and f2: 32
space between x and f2: -52
space between g and x: 12
space between x and y: 12
space between g and y: 8
 19
Author: Eric Wang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-12 15:58:10

El relleno y el embalaje son solo dos aspectos de la misma cosa:

  • embalaje o alineación es el tamaño al que se redondea cada miembro
  • el relleno es el espacio adicional añadido para que coincida con la alineación

En mystruct_A, asumiendo una alineación predeterminada de 4, cada miembro se alinea en un múltiplo de 4 bytes. Dado que el tamaño de char es 1, el relleno para a y c es 4 - 1 = 3 bytes mientras que no se requiere relleno para int b que ya es de 4 bytes. Funciona igual camino para mystruct_B.

 4
Author: casablanca,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-29 17:23:05

El empaquetamiento de la estructura solo se realiza cuando le dices explícitamente a tu compilador que empaquete la estructura. Relleno es lo que estás viendo. Su sistema de 32 bits está rellenando cada campo a la alineación de palabras. Si le hubieras dicho a tu compilador que empaquetara las estructuras, serían de 6 y 5 bytes, respectivamente. Pero no hagas eso. No es portable y hace que los compiladores generen código mucho más lento (y a veces incluso con errores).

 1
Author: nmichaels,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-29 17:23:29

La alineación de la estructura de datos es la forma en que se organizan los datos y se accede a ellos en la memoria de la computadora. Consiste en dos problemas separados pero relacionados: alineación de datos y relleno de estructura de datos. Cuando una computadora moderna lee o escribe en una dirección de memoria, lo hará en trozos del tamaño de una palabra (por ejemplo, trozos de 4 bytes en un sistema de 32 bits) o más. Alineación de datos significa poner los datos en una dirección de memoria igual a algún múltiplo del tamaño de la palabra, lo que aumenta el rendimiento del sistema debido a la forma en que la CPU maneja la memoria. Para alinear los datos, puede ser necesario insertar algunos bytes sin sentido entre el final de la última estructura de datos y el comienzo de la siguiente, que es relleno de estructura de datos.

  1. Para alinear los datos en la memoria, uno o más bytes vacíos (direcciones) se insertan (o se dejan vacíos) entre las direcciones de memoria que se asignan para otros miembros de la estructura mientras que la asignación de memoria. Este concepto se llama relleno de estructura.
  2. Arquitectura de un el procesador de la computadora es de tal manera que puede leer 1 palabra (4 bytes en un procesador de 32 bits) de la memoria a la vez.
  3. Para hacer uso de esta ventaja del procesador, los datos siempre se alinean como un paquete de 4 bytes que conduce a insertar direcciones vacías entre la dirección de otro miembro.
  4. Debido a este concepto de relleno de estructura en C, el tamaño de la estructura no es siempre el mismo que pensamos.
 -1
Author: manoj yadav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-14 09:07:53