C struct disposition de la mémoire

J’ai un fond C #. Je suis très novice dans un langage 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éordonner les champs de données ou append des bits supplémentaires entre les champs de manière implicite. Donc, j’ai dû spécifier un atsortingbut spécial pour remplacer ce comportement pour une mise en page exacte.

AFAIK, C ne réorganise pas ou n’aligne pas la disposition mémoire d’une 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? Que faut-il réorganiser / aligner et non?

En C, le compilateur est autorisé à dicter un certain alignement pour chaque type primitif. L’alignement correspond généralement à la taille du type. Mais c’est entièrement spécifique à la mise en œuvre.

Les octets de remplissage sont introduits pour que chaque object soit correctement aligné. La réorganisation n’est pas autorisée.

Il est possible que chaque compilateur moderne à distance implémente #pragma pack qui permet de contrôler le remplissage et laisse au programmeur le soin de se conformer à l’ABI. (Il est ssortingctement non standard, cependant.)

De C99 §6.7.2.1:

12 Chaque membre non-bit-field d’un object structure ou union est aligné d’une manière adaptée à son type.

13 Dans un object structure, les membres des champs non binarys et les unités dans lesquelles résident les champs binarys ont des adresses qui augmentent dans l’ordre dans lequel ils sont déclarés. Un pointeur vers un object 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 object de structure, mais pas au début.

C’est spécifique à la mise en œuvre, mais en pratique, la règle (en l’absence de #pragma pack ou similaire) est:

  • Les membres Struct sont stockés dans l’ordre dans lequel ils sont déclarés. (Ceci est requirejs par la norme C99, comme mentionné précédemment).
  • Si nécessaire, un remplissage est ajouté avant chaque membre struct, afin de garantir un alignement correct.
  • Chaque primitive de type T nécessite un alignement de sizeof(T) octets sizeof(T) .

Donc, étant donné la structure suivante:

 struct ST { char ch1; short s; char ch2; long long ll; int i; }; 
  • ch1 est à l’offset 0
  • un octet de remplissage est inséré pour aligner …
  • s au décalage 2
  • ch2 est au décalage 4, immédiatement après s
  • 3 octets de remplissage sont insérés pour aligner …
  • ll à l’offset 8
  • i au décalage 16, juste après ll
  • 4 octets de remplissage sont ajoutés à la fin de sorte que la structure globale soit un multiple de 8 octets. Je l’ai vérifié sur un système 64 bits: les systèmes 32 bits peuvent permettre aux structures d’avoir un alignement sur 4 octets.

Donc sizeof(ST) est 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 l’ article de Wikipédia sur l’ alignement de la structure de données pour mieux comprendre l’alignement des données.

De l’ 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 du mot, ce qui augmente les performances du système en raison de la manière 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 de la suivante, qui est le remplissage de la structure de données.

From 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 faible puissance de deux et spécifie le nouvel alignement en octets.

  1. pragma pack (n) définit simplement le nouvel alignement.

  2. pragma pack () définit l’alignement sur celui qui était dans

    Effet lors du démarrage de la compilation (voir aussi l’option de ligne de commande -fpack-struct [=] voir Options de Code Gen).

  3. pragma pack (push [, n]) pousse le paramètre d’alignement actuel sur un

    stack interne et définit éventuellement le nouvel alignement.

  4. pragma pack (pop) restaure le paramètre d’alignement sur celui enregistré dans

    le haut de la stack interne (et supprime cette entrée de stack). Notez que enter code here #pragma pack ([n]) n’influence pas cette stack interne; il est donc possible d’avoir #pragma pack (push) suivi de plusieurs instances #pragma pack (n) et finalisé par un seul pack #pragma (pop).

Certaines cibles, par exemple i386 et powerpc, prennent en charge le paramètre #pragma ms_struct qui présente une structure sous la forme de __atsortingbute__ ((ms_struct)) documentée.

  1. pragma ms_struct active la disposition des structures déclarées.

  2. pragma ms_struct désactive la disposition des structures déclarées.

  3. pragma ms_struct reset revient à la mise en page par défaut.