2010-05-01 15 views
61

Tengo el fondo C#. Muy novato al lenguaje de bajo nivel como C.Disposición de la memoria C struct?

En C#, la memoria de struct se distribuye por compilador de manera predeterminada. El compilador puede volver a ordenar campos de datos o rellenar bits adicionales entre campos implícitamente. Así que tuve que especificar algún atributo especial para anular este comportamiento para el diseño exacto.

AFAIK, C no reordena o alinea el diseño de memoria de una estructura de forma predeterminada. Pero escuché que hay algunas excepciones que son muy difíciles de encontrar.

¿Cuál es el comportamiento de la disposición de memoria de C? (lo que debe reordenarse/alinearse y no)

Respuesta

81

En C, se permite que el compilador de dictar algunos de alineación para cada tipo primitivo. Por lo general, la alineación es del tamaño del tipo. Pero es completamente específico de la implementación.

Los bytes de relleno se introducen para que todos los objetos se alineen correctamente. Reordenar no está permitido.

Posiblemente cada compilador remotamente moderno implementa #pragma pack que permite el control del relleno y deja que el programador cumpla con la ABI. (Es estrictamente no estándar, sin embargo.)

De C99 §6.7.2.1:

12 Cada miembro no campo de bits de un estructura o unión objeto está alineado de una manera implementación- definido apropiado para su tipo.

13 Dentro de una estructura de objetos , la no campo de bits miembros y las unidades en que campos de bits residir tener direcciones que aumento en el orden en que se declaran . Un puntero a un objeto de estructura , adecuadamente convertido, apunta a su miembro inicial (o si ese miembro es un campo de bits, luego a la unidad en que reside), y viceversa. Puede haber un relleno sin nombre dentro de un objeto de estructura , pero no en su inicio .

+0

Algunos compiladores (es decir, GCC) implementan el mismo efecto que '#pragma pack' pero con un control más detallado sobre la semántica. –

+14

Estoy sorprendido de ver un voto negativo. ¿Alguien puede señalar el error? – Potatoswatter

+0

Gracias por su cuidado. Actualicé la pregunta mientras guiaste. – Eonil

8

Puede comenzar por leer el data structure alignment wikipedia article para obtener una mejor comprensión de la alineación de datos.

Desde el wikipedia article:

alineación de datos significa poner los datos a un desplazamiento igual a un múltiplo del tamaño de la palabra, lo que aumenta el rendimiento del sistema de memoria debido a la forma en que la CPU se encarga de 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 inicio de la siguiente, que es el relleno de la estructura de datos.

De 6.54.8 Structure-Packing Pragmas de la documentación de GCC:

Para la compatibilidad con Microsoft compiladores de Windows, GCC soporta un conjunto de directivas #pragma que cambian la alineación máximo de miembros del estructuras (que no sea zero-width bitfields), uniones y clases posteriormente definidas. El n valor a continuación siempre requiere una pequeña potencia de dos y especifica la nueva alineación en bytes.

  1. pragma pack (n) simplemente establece la nueva alineación.

  2. paquete pragma() establece la alineación a la que estaba en

    efecto cuando comenzó la compilación (ver comando también la opción de línea de -fpack-estructura [=] Ver el Código de Opciones GEN).
  3. paquete pragma (push [, n]) empuja el ajuste de la alineación actual en una pila interna

    y luego opcionalmente coloca la nueva alineación.
  4. paquete pragma (pop) restaura el ajuste de la alineación para el salvado en

    la parte superior de la pila interna (y quita que entrada de la pila). Tenga en cuenta que enter code here #pragma pack ([n]) no influye en esta pila interna; así es posible tener #pragma pack (push) seguido de múltiples #pragma pack (n) instancias y finalizado por un solo #pragma pack (pop).

Algunos objetivos, p. Ej.i386 y powerpc, admiten el ms_struct #pragma que establece una estructura como documentada __attribute__ ((ms_struct)).

  1. pragma ms_struct de vueltas en el diseño de estructuras declaradas.

  2. pragma ms_struct off desactiva el diseño de las estructuras declaradas.

  3. pragma ms_struct reset vuelve al diseño predeterminado.

+0

Gracias por la atención. Modifiqué la pregunta mientras guiaste. – Eonil

2

En C, las estructuras se disponen casi exactamente como se especifica en el código. Similar a StructLayout.Sequential de C#.

La única diferencia está en la alineación de miembros. Esto nunca reordena los miembros de datos en una estructura, pero puede cambiar el tamaño de la estructura al insertar bytes "pad" en el medio de la estructura. La razón para esto es asegurarse de que cada uno de los miembros comience en un límite (generalmente 4 u 8 bytes).

Por ejemplo:

struct mystruct { 
    int a; 
    short int b; 
    char c; 
}; 

El tamaño de esta estructura suele ser de 12 bytes (4 por cada miembro). Esto se debe a que la mayoría de los compiladores, de forma predeterminada, harán que cada miembro tenga el mismo tamaño que el más grande de la estructura. Entonces el char ocupará 4 bytes en lugar de uno. Pero es muy importante tener en cuenta que sizeof (mystruct :: c) seguirá en 1, pero sizeof (mystruct) será 12.

Puede ser difícil predecir cómo el compilador rellenará/alineará la estructura .La mayoría se ajustará a los valores predeterminados como he explicado anteriormente, algunos no incluirán padding/alignment (también llamado "packed").

El método para alterar este comportamiento depende en gran medida del compilador, no hay nada en el lenguaje que especifique cómo se debe manejar esto. En MSVC usaría #pragma pack(1) para desactivar la alineación (el 1 dice alinear todo en los límites de 1 byte). EN GCC usaría __attribute__((packed)) en la definición de estructura. Consulte la documentación de su compilador para ver qué hace de manera predeterminada y cómo cambiar ese comportamiento.

+3

Uh, 'sizeof (struct mystruct)' imprime 8 en mi sistema. C no alinea a todos los miembros con la alineación del miembro más grande, alinea a todos los miembros con su alineación y luego alinea la estructura con la alineación del miembro más grande. –

+0

Uh, como dije, depende del compilador. – SoapBox

+11

Soapbox: No si ningún compilador lo hace de esa manera. – Potatoswatter

88

Es específico de la implementación, pero en la práctica la regla (en ausencia de #pragma pack o similar) es:

  • miembros de la estructura se almacenan en el orden en que se declaran. (Esto es requerido por el estándar C99, como se mencionó anteriormente).
  • Si es necesario, se agrega relleno antes de cada miembro de la estructura, para asegurar la alineación correcta.
  • Cada tipo primitivo T requiere una alineación de sizeof(T) bytes.

Por lo tanto, dada la siguiente estructura:

struct ST 
{ 
    char ch1; 
    short s; 
    char ch2; 
    long long ll; 
    int i; 
}; 
  • CH1 está en la posición 0
  • se inserta un byte de relleno para alinear ...
  • s en desplazamiento 2
  • ch2 está en el desplazamiento 4, inmediatamente después de s
  • 3 bytes de relleno se insertan para alinear ...
  • ll en el offset 8
  • i está en el offset 16, justo después de ll se añaden
  • 4 bytes de relleno al final de modo que la estructura general es un múltiplo de 8 bytes. Comprobé esto en un sistema de 64 bits: los sistemas de 32 bits pueden permitir que las estructuras tengan una alineación de 4 bytes.

Así es sizeof(ST) 24.

Puede ser reducido a 16 bytes por la reordenación de los miembros para evitar el relleno:

struct ST 
{ 
    long long ll; // @ 0 
    int i;  // @ 8 
    short s;  // @ 12 
    char ch1;  // @ 14 
    char ch2;  // @ 15 
} ST; 
+3

Si es necesario, se agrega relleno antes de ...Más como después. Mejor agrega un miembro final de "char" a tu ejemplo. – Deduplicator

+3

Un tipo primitivo no requiere necesariamente una alineación de bytes 'sizeof (T)'. Por ejemplo, un 'doble' en las arquitecturas comunes de 32 bits es de 8 bytes pero [a menudo solo requiere una alineación de 4 bytes] (http://stackoverflow.com/a/11110283/706054). Además, el relleno al final de la estructura solo rellena la alineación del miembro de estructura más ancho. Por ejemplo, una estructura de 3 variables char podría no tener relleno. – Matt