Pregunta ¿Por qué no es sizeof para una estructura igual a la suma de sizeof de cada miembro?


¿Por qué el operador 'sizeof' devuelve un tamaño mayor para una estructura que el tamaño total de los miembros de la estructura?


547
2017-09-23 04:24


origen


Respuestas:


Esto se debe al relleno añadido para satisfacer las restricciones de alineación. Alineación de estructura de datos afecta el rendimiento y la corrección de los programas:

  • El acceso mal alineado puede ser un error difícil (a menudo SIGBUS)
  • El acceso desalineado podría ser un error suave.
    • O corregido en hardware, para una degradación modesta del rendimiento.
    • O corregido por emulación en el software, para una degradación de rendimiento severa.
    • Además, la atomicidad y otras garantías de concurrencia pueden romperse, lo que lleva a errores sutiles.

Aquí hay un ejemplo usando la configuración típica para un procesador x86 (todos los modos usados ​​de 32 y 64 bits):

struct X
{
    short s; /* 2 bytes */
             /* 2 padding bytes */
    int   i; /* 4 bytes */
    char  c; /* 1 byte */
             /* 3 padding bytes */
};

struct Y
{
    int   i; /* 4 bytes */
    char  c; /* 1 byte */
             /* 1 padding byte */
    short s; /* 2 bytes */
};

struct Z
{
    int   i; /* 4 bytes */
    short s; /* 2 bytes */
    char  c; /* 1 byte */
             /* 1 padding byte */
};

const int sizeX = sizeof(struct X); /* = 12 */
const int sizeY = sizeof(struct Y); /* = 8 */
const int sizeZ = sizeof(struct Z); /* = 8 */

Se puede minimizar el tamaño de las estructuras clasificando los miembros por alineación (basta ordenar por tamaño para eso en los tipos básicos) (como la estructura Z en el ejemplo anterior).

NOTA IMPORTANTE: Las normas C y C ++ establecen que la alineación de la estructura está definida por la implementación. Por lo tanto, cada compilador puede elegir alinear los datos de forma diferente, lo que da como resultado diseños de datos diferentes e incompatibles. Por esta razón, cuando se trata de bibliotecas que serán utilizadas por diferentes compiladores, es importante entender cómo los compiladores alinean los datos. Algunos compiladores tienen configuraciones de línea de comandos y / o especiales #pragma declaraciones para cambiar la configuración de alineación de la estructura.


542
2017-09-23 04:27



Alineación de paquetes y bytes, como se describe en las Preguntas frecuentes C aquí:

Es para la alineación. Muchos procesadores no pueden acceder a 2 y 4 bytes   cantidades (por ejemplo, entradas y entradas largas) si están abarrotadas   en todos los sentidos.

Supongamos que tiene esta estructura:

struct {
    char a[3];
    short int b;
    long int c;
    char d[3];
};

Ahora, podrías pensar que debería ser posible empacar esto   estructura en la memoria de esta manera:

+-------+-------+-------+-------+
|           a           |   b   |
+-------+-------+-------+-------+
|   b   |           c           |
+-------+-------+-------+-------+
|   c   |           d           |
+-------+-------+-------+-------+

Pero es mucho, mucho más fácil para el procesador si el compilador organiza   es así:

+-------+-------+-------+
|           a           |
+-------+-------+-------+
|       b       |
+-------+-------+-------+-------+
|               c               |
+-------+-------+-------+-------+
|           d           |
+-------+-------+-------+

En la versión empaquetada, observe cómo es al menos un poco difícil   usted y yo para ver cómo se envuelven los campos byc? En una palabra,   es difícil para el procesador, también. Por lo tanto, la mayoría de los compiladores rellenarán   la estructura (como con campos adicionales, invisibles) como esta:

+-------+-------+-------+-------+
|           a           | pad1  |
+-------+-------+-------+-------+
|       b       |     pad2      |
+-------+-------+-------+-------+
|               c               |
+-------+-------+-------+-------+
|           d           | pad3  |
+-------+-------+-------+-------+

146
2017-09-23 04:31



Si desea que la estructura tenga un cierto tamaño con GCC, por ejemplo, use __attribute__((packed)).

En Windows, puede establecer la alineación en un byte al usar el compilador cl.exe con el / Zp opción.

Por lo general, es más fácil para la CPU acceder a datos que son múltiplos de 4 (u 8), dependiendo de la plataforma y también del compilador.

Entonces, básicamente se trata de una alineación.

Debes tener buenas razones para cambiarlo.


23
2018-05-31 09:27



Esto puede deberse a la alineación de bytes y al relleno para que la estructura tenga un número par de bytes (o palabras) en su plataforma. Por ejemplo, en C en Linux, las siguientes 3 estructuras:

#include "stdio.h"


struct oneInt {
  int x;
};

struct twoInts {
  int x;
  int y;
};

struct someBits {
  int x:2;
  int y:6;
};


int main (int argc, char** argv) {
  printf("oneInt=%zu\n",sizeof(struct oneInt));
  printf("twoInts=%zu\n",sizeof(struct twoInts));
  printf("someBits=%zu\n",sizeof(struct someBits));
  return 0;
}

Tener miembros cuyos tamaños (en bytes) son 4 bytes (32 bits), 8 bytes (2x 32 bits) y 1 byte (2 + 6 bits), respectivamente. El programa anterior (en Linux usando gcc) imprime los tamaños como 4, 8 y 4, donde la última estructura está acolchada para que sea una sola palabra (bytes de 4 x 8 bits en mi plataforma de 32 bits).

oneInt=4
twoInts=8
someBits=4

11
2018-06-10 15:07



Ver también:

para Microsoft Visual C:

http://msdn.microsoft.com/en-us/library/2e70t5y1%28v=vs.80%29.aspx

y GCC reclaman compatibilidad con el compilador de Microsoft .:

http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html

Además de las respuestas anteriores, tenga en cuenta que independientemente del embalaje, no hay miembros-orden-garantía en C ++. Los compiladores pueden (y ciertamente lo hacen) agregar puntero de tabla virtual y miembros de estructuras base a la estructura. Incluso la norma no garantiza la existencia de la tabla virtual (no se especifica la implementación del mecanismo virtual) y, por lo tanto, se puede concluir que dicha garantía es simplemente imposible.

Estoy bastante seguro orden de miembros es garantizado en C, pero no lo contaría, al escribir un programa multiplataforma o de compilación cruzada.


9
2017-09-23 04:27



El tamaño de una estructura es mayor que la suma de sus partes debido a lo que se llama empaque. Un procesador en particular tiene un tamaño de datos preferido con el que funciona. El tamaño preferido de los procesadores más modernos es 32 bits (4 bytes). Acceder a la memoria cuando los datos están en este tipo de límite es más eficiente que las cosas que se extienden a través de ese límite de tamaño.

Por ejemplo. Considera la estructura simple:

struct myStruct
{
   int a;
   char b;
   int c;
} data;

Si la máquina es una máquina de 32 bits y los datos están alineados en un límite de 32 bits, vemos un problema inmediato (suponiendo que no hay alineación de estructura). En este ejemplo, supongamos que los datos de estructura comienzan en la dirección 1024 (0x400), tenga en cuenta que los 2 bits más bajos son cero, por lo que los datos están alineados con un límite de 32 bits. El acceso a data.a funcionará bien porque comienza en un límite - 0x400. El acceso a data.b también funcionará bien, porque está en la dirección 0x404, otro límite de 32 bits. Pero una estructura desalineada pondría data.c en la dirección 0x405. Los 4 bytes de data.c están en 0x405, 0x406, 0x407, 0x408. En una máquina de 32 bits, el sistema leería data.c durante un ciclo de memoria, pero solo obtendría 3 de los 4 bytes (el 4to byte está en el siguiente límite). Entonces, el sistema tendría que hacer un segundo acceso a memoria para obtener el 4to byte,

Ahora, si en lugar de poner data.c en la dirección 0x405, el compilador rellenó la estructura en 3 bytes y puso data.c en la dirección 0x408, entonces el sistema solo necesitaría 1 ciclo para leer los datos, reduciendo el tiempo de acceso a ese elemento de datos en un 50%. Padding intercambia la eficiencia de la memoria para la eficiencia del procesamiento. Dado que las computadoras pueden tener grandes cantidades de memoria (muchos gigabytes), los compiladores consideran que el intercambio (velocidad sobre el tamaño) es razonable.

Desafortunadamente, este problema se convierte en un asesino cuando intenta enviar estructuras a través de una red o incluso escribir los datos binarios en un archivo binario. El relleno insertado entre elementos de una estructura o clase puede interrumpir los datos enviados al archivo o red. Para escribir un código portátil (uno que vaya a varios compiladores diferentes), probablemente tendrá que acceder a cada elemento de la estructura por separado para garantizar el "embalaje" adecuado.

Por otro lado, los diferentes compiladores tienen diferentes habilidades para gestionar el embalaje de la estructura de datos. Por ejemplo, en Visual C / C ++ el compilador admite el comando #pragma pack. Esto le permitirá ajustar el empaque y la alineación de datos.

Por ejemplo:

#pragma pack 1
struct MyStruct
{
    int a;
    char b;
    int c;
    short d;
} myData;

I = sizeof(myData);

Ahora debería tener la longitud de 11. Sin el pragma, podría ser cualquier cosa entre 11 y 14 (y para algunos sistemas, tanto como 32), dependiendo del empaque predeterminado del compilador.


6
2018-05-04 15:38



Puede hacerlo si ha establecido implícita o explícitamente la alineación de la estructura. Una estructura que está alineada 4 siempre será un múltiplo de 4 bytes, incluso si el tamaño de sus miembros sería algo que no es un múltiplo de 4 bytes.

También se puede compilar una biblioteca en x86 con entradas de 32 bits y es posible que esté comparando sus componentes en un proceso de 64 bits que le daría un resultado diferente si lo hiciera a mano.


5
2017-09-23 13:38



Calado estándar C99 N1256

http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf

6.5.3.4 El operador de sizeof:

3 Cuando se aplica a un operando que tiene estructura o tipo de unión,   el resultado es el número total de bytes en dicho objeto,   incluyendo relleno interno y posterior.

6.7.2.1 Estructura y especificadores de unión:

13 ... Puede haber un nombre sin nombre   relleno dentro de un objeto estructural, pero no al principio.

y:

15 Puede haber un relleno sin nombre al final de una estructura o unión.

El nuevo C99 característica de miembro de matriz flexible (struct S {int is[];};) también puede afectar el relleno:

16 Como un caso especial, el último elemento de una estructura con más de un miembro nombrado puede   tener un tipo de matriz incompleta; esto se llama un miembro de matriz flexible. En la mayoría de las situaciones,   el miembro de matriz flexible se ignora. En particular, el tamaño de la estructura es como si el   miembro de matriz flexible se omitieron excepto que puede tener más relleno posterior que   la omisión implicaría.

Anexo J Cuestiones de portabilidad reitera:

Los siguientes no están especificados: ...

  • El valor de los bytes de relleno cuando se almacenan valores en estructuras o uniones (6.2.6.1)

Calado estándar C ++ 11 N3337

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf

5.3.3 Sizeof:

2 Cuando se aplica   a una clase, el resultado es la cantidad de bytes en un objeto de esa clase, incluido el relleno necesario para   colocando objetos de ese tipo en una matriz.

9.2 Miembros de la clase:

Un puntero a un objeto struct de diseño estándar, adecuadamente convertido usando un reinterpret_cast, apunta a su   miembro inicial (o si ese miembro es un campo de bits, luego a la unidad en la que reside) y viceversa. [ Nota:   Por lo tanto, podría haber un relleno sin nombre dentro de un objeto de estructura de disposición estándar, pero no al principio,   como sea necesario para lograr la alineación apropiada. - nota final]

Solo sé lo suficiente de C ++ para entender la nota :-)


5
2017-07-28 21:25



Además de las otras respuestas, una estructura puede (pero generalmente no) tener funciones virtuales, en cuyo caso el tamaño de la estructura también incluirá el espacio para la vtbl.


4
2018-02-24 06:46