Diseño de memoria C struct

Tengo un fondo de C #. Soy muy novato en un lenguaje de bajo nivel como C.

En C #, la memoria de struct es establecida por el comstackdor de forma predeterminada. El comstackdor puede reordenar campos de datos o rellenar bits adicionales entre campos implícitamente. Entonces, 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 struct de forma predeterminada. Sin embargo, escuché que hay una pequeña excepción que es muy difícil de encontrar.

¿Cuál es el comportamiento de diseño de memoria de C? ¿Qué debería reordenarse / alinearse y no?

En C, el comstackdor puede dictar alguna 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 estén alineados correctamente. Reordenar no está permitido.

Es posible que todos los comstackdores remotamente modernos implementen el #pragma pack que permite controlar el relleno y deja que el progtwigdor cumpla con el ABI. (Es estrictamente no estándar, sin embargo)

De C99 §6.7.2.1:

12 Cada miembro de campo no de bit de una estructura u objeto de unión se alinea de una manera definida por la implementación apropiada para su tipo.

13 Dentro de un objeto de estructura, los miembros de campo que no son de bit y las unidades en que residen los campos de bit tienen direcciones que aumentan 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 la que reside), y viceversa. Puede haber un relleno sin nombre dentro de un objeto de estructura, pero no al principio.

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

  • Los miembros de Struct 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.

Entonces, dada la siguiente estructura:

 struct ST { char ch1; short s; char ch2; long long ll; int i; }; 
  • ch1 está en el desplazamiento 0
  • un byte relleno se inserta para alinear …
  • s en el desplazamiento 2
  • ch2 está en el desplazamiento 4, inmediatamente después de s
  • 3 bytes de relleno se insertan para alinear …
  • ll en offset 8
  • i en el desplazamiento 16, justo después de ll
  • Se agregan 4 bytes de relleno al final para que la estructura general sea 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.

Entonces sizeof(ST) es 24.

Se puede reducir a 16 bytes reorganizando 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; 

Puede comenzar leyendo el artículo de wikipedia de alineación de estructura de datos para obtener una mejor comprensión de la alineación de datos.

Del artículo de wikipedia :

La alineación de datos significa poner los datos en un desplazamiento de memoria igual a un múltiplo del tamaño de 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 inicio de la siguiente, que es el relleno de la estructura de datos.

De 6.54.8 Pragmas de empaque de estructura de la documentación de GCC:

Para la compatibilidad con los comstackdores de Microsoft Windows, GCC admite un conjunto de directivas #pragma que cambian la alineación máxima de los miembros de las estructuras (distintos de los campos de bits de ancho cero), las uniones y las clases definidas posteriormente. Siempre se requiere que el valor n de debajo sea 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. pragma pack () establece la alineación con el que estaba en

    efecto cuando se inició la comstackción (ver también la opción de línea de comando -fpack-struct [=] ver Opciones de Código Gen).

  3. pragma pack (push [, n]) empuja la configuración de alineación actual en un

    stack interna y luego opcionalmente establece la nueva alineación.

  4. pragma pack (pop) restaura la configuración de alineación a la guardada en

    la parte superior de la stack interna (y elimina esa entrada de la stack). Tenga en cuenta que enter code here #pragma pack ([n]) no influye en esta stack interna; por lo tanto, es posible tener #pragma pack (push) seguido de múltiples #pragma pack (n) instancias y finalizado por un solo paquete #pragma (pop).

Algunos objectives, por ejemplo, i386 y powerpc, son compatibles con ms_struct #pragma, que establece una estructura como documentado __attribute__ ((ms_struct)).

  1. pragma ms_struct on activa el diseño de las 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.