initialize - Diseño de memoria C struct?
structs in java (3)
Tengo antecedentes de C #. Muy novato al lenguaje de bajo nivel como C.
En C #, la memoria de struct se distribuye por compilador de forma 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 diseño de memoria de C? (lo que debe ser reordenado / alineado y no)
En C, el compilador 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 compiladores remotamente modernos implementen el #pragma pack
que permite controlar el relleno y deja que el programador 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 compiladores 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.
pragma pack (n) simplemente establece la nueva alineación.
pragma pack () establece la alineación con el que estaba en
efecto cuando se inició la compilación (ver también la opción de línea de comando -fpack-struct [=] ver Opciones de Código Gen).pragma pack (push [, n]) empuja la configuración de alineación actual en un
pila interna y luego opcionalmente establece la nueva alineación.pragma pack (pop) restaura la configuración de alineación a la guardada en
la parte superior de la pila interna (y elimina esa entrada de la pila). Tenga en cuenta queenter code here
#pragma pack ([n]) no influye en esta pila 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 objetivos, por ejemplo, i386 y powerpc, son compatibles con ms_struct #pragma, que establece una estructura como documentado __attribute__ ((ms_struct)).
pragma ms_struct on activa el diseño de las estructuras declaradas.
pragma ms_struct off desactiva el diseño de las estructuras declaradas.
pragma ms_struct reset vuelve al diseño predeterminado.