J'ai un fond C #. Je suis vraiment un débutant dans une langue de bas niveau comme C.
En C #, la mémoire de struct
est définie par défaut par le compilateur. Le compilateur peut réorganiser les champs de données ou ajouter des bits supplémentaires entre les champs de manière implicite. J'ai donc dû spécifier un attribut spécial pour redéfinir ce comportement pour une présentation exacte.
Autant que je sache, C ne réorganise ni n’aligne la structure de mémoire d’un struct
par défaut. Cependant, j'ai entendu dire qu'il y avait une petite exception très difficile à trouver.
Quel est le comportement de la mémoire de C? Qu'est-ce qui devrait être réordonné/aligné et non?
En C, le compilateur est autorisé à dicter un alignement pour chaque type de primitive. En règle générale, l'alignement correspond à la taille du type. Mais cela dépend entièrement de la mise en œuvre.
Des octets de remplissage sont introduits pour que chaque objet soit correctement aligné. La réorganisation n'est pas autorisée.
Peut-être chaque compilateur moderne à distance implémente-t-il #pragma pack
qui permet de contrôler le remplissage et laisse le programmeur se conformer à l’ABI. (C'est strictement non standard, cependant.)
À partir de C99 § 6.7.2.1:
12 Chaque membre de champ non binaire d'un objet de structure ou d'union est aligné d'une manière définie par la mise en œuvre et adaptée à son type.
13 Dans un objet de structure, les membres autres que les champs de bits et les unités dans lesquelles résident les champs de bits ont des adresses qui augmentent dans l'ordre dans lequel ils ont été déclarés. Un pointeur sur un objet de structure, converti de manière appropriée, pointe vers son membre initial (ou si ce membre est un champ de bits, puis vers l'unité dans laquelle il réside), et inversement. Il peut y avoir un remplissage non nommé dans un objet de structure, mais pas à son début.
C'est spécifique à l'implémentation, mais en pratique la règle (en l'absence de #pragma pack
Ou similaire) est la suivante:
sizeof(T)
octets.Donc, étant donné la structure suivante:
struct ST
{
char ch1;
short s;
char ch2;
long long ll;
int i;
};
ch1
Est à l'offset 0s
au décalage 2ch2
Est à l'offset 4, immédiatement après sll
à l'offset 8i
est à l'offset 16, juste après llDonc, sizeof(ST)
vaut 24.
Il peut être réduit à 16 octets en réorganisant les membres pour éviter le remplissage:
struct ST
{
long long ll; // @ 0
int i; // @ 8
short s; // @ 12
char ch1; // @ 14
char ch2; // @ 15
} ST;
Vous pouvez commencer par lire le article de wikipedia sur l'alignement de la structure des données pour mieux comprendre l'alignement des données.
De la article de wikipedia :
L'alignement des données consiste à placer les données à un décalage de mémoire égal à un multiple de la taille de Word, ce qui augmente les performances du système en raison de la façon dont le processeur gère la mémoire. Pour aligner les données, il peut être nécessaire d'insérer des octets sans signification entre la fin de la dernière structure de données et le début du suivant, qui correspond au remplissage de la structure de données.
D'après 6.54.8 Structure-Emballage Pragmas de la documentation GCC:
Pour assurer la compatibilité avec les compilateurs Microsoft Windows, GCC prend en charge un ensemble de directives #pragma qui modifient l'alignement maximal des membres des structures (autres que les champs de bits de largeur nulle), des unions et des classes définies ultérieurement. La valeur n ci-dessous doit toujours être une petite puissance de deux et spécifie le nouvel alignement en octets.
#pragma pack(n)
définit simplement le nouvel alignement.#pragma pack()
définit l'alignement sur celui qui était en vigueur au début de la compilation (voir aussi l'option de ligne de commande -fpack-struct [=] voir Options de génération de code).#pragma pack(Push[,n])
applique le réglage d'alignement actuel sur une pile interne, puis définit éventuellement le nouvel alignement.#pragma pack(pop)
restaure le paramètre d'alignement sur celui enregistré en haut de la pile interne (et supprime cette entrée de pile). Notez que#pragma pack([n])
n'influence pas cette pile interne; ainsi, il est possible d'avoir#pragma pack(Push)
suivi de plusieurs instances de#pragma pack(n)
et finalisé par un seul#pragma pack(pop)
.Certaines cibles, par exemple i386 et powerpc, prennent en charge la ms_struct
#pragma
, qui établit une structure sous la forme documentée__attribute__ ((ms_struct))
.
#pragma ms_struct on
Active la mise en page des structures déclarées.#pragma ms_struct off
Désactive la mise en page pour les structures déclarées.#pragma ms_struct reset
Revient à la disposition par défaut.