Alignement de la mémoire dans les structures C

Je travaille sur la machine 32 bits, donc je suppose que l’alignement de la mémoire doit être de 4 octets. Disons que j’ai struct:

typedef struct { unsigned short v1; unsigned short v2; unsigned short v3; } myStruct; 

la taille réelle est de 6 octets, et je suppose que la taille alignée devrait être de 8, mais sizeof(myStruct) me renvoie 6.

Cependant si j’écris:

 typedef struct { unsigned short v1; unsigned short v2; unsigned short v3; int i; } myStruct; 

la taille réelle est de 10 octets, alignée sur 12, et cette fois-ci sizeof(myStruct) == 12 .

Quelqu’un peut-il expliquer quelle est la différence?

Au moins sur la plupart des machines, un type est seulement aligné sur une limite aussi grande que le type lui-même [Edit: vous ne pouvez pas vraiment exiger un alignement “plus” car vous devez pouvoir créer des tableaux, et vous ne peut pas insérer de remplissage dans un tableau]. Sur votre implémentation, short est apparemment 2 octets, et int 4 octets.

Cela signifie que votre première structure est alignée sur une limite de 2 octets. Comme tous les membres sont 2 octets chacun, aucun remplissage n’est inséré entre eux.

Le second contient un élément de 4 octets, aligné sur une limite de 4 octets. Comme il est précédé de 6 octets, 2 octets de remplissage sont insérés entre v3 et i , ce qui donne 6 octets de données dans le short , deux octets de remplissage et 4 octets de données supplémentaires dans le total pour un total de 12.

Oubliez les différents membres, même si vous écrivez deux structures dont les membres sont exactement les mêmes, à la différence que l’ordre dans lequel elles sont déclarées est différent, alors la taille de chaque structure peut être (et est souvent) différente.

Par exemple, voir ceci,

 #include  using namespace std; struct A { char c; char d; int i; }; struct B { char c; int i; //note the order is different! char d; }; int main() { cout << sizeof(A) << endl; cout << sizeof(B) << endl; } 

Comstackz-le avec gcc-4.3.4 et vous obtenez cette sortie:

 8 12 

Autrement dit, les tailles sont différentes même si les deux structures ont les mêmes membres!

Code chez Ideone: http://ideone.com/HGGVl

L'essentiel est que le standard ne parle pas de la manière dont le remplissage doit être fait, et donc les compilateurs sont libres de prendre toute décision et vous ne pouvez pas supposer que tous les compilateurs prennent la même décision.

Par défaut, les valeurs sont alignées en fonction de leur taille. Donc, une valeur de 2 octets comme un short est alignée sur une limite de 2 octets, et une valeur de 4 octets comme un int est alignée sur une limite de 4 octets

Dans votre exemple, 2 octets de remplissage sont ajoutés avant i pour garantir que i tombe sur une limite de 4 octets.

(La structure entière est alignée sur une limite au moins aussi grande que la plus grande valeur de la structure, donc votre structure sera alignée sur une limite de 4 octets.)

Les règles réelles varient en fonction de la plate-forme – la page Wikipedia sur l’ alignement de la structure des données contient plus de détails.

Les compilateurs vous permettent généralement de contrôler le conditionnement via (par exemple) #pragma pack directives #pragma pack .

Tout d’abord, alors que les spécificités du remplissage sont laissées au compilateur, le système d’exploitation impose également certaines règles en matière d’alignement. Cette réponse suppose que vous utilisez gcc, bien que le système d’exploitation puisse varier

Pour déterminer l’espace occupé par une structure donnée et ses éléments, vous pouvez suivre ces règles:

Tout d’abord, supposons que la structure commence toujours à une adresse correctement alignée pour tous les types de données.

Ensuite, pour chaque entrée de la structure:

  • L’espace minimum requirejs est la taille brute de l’élément donnée par sizeof(element) .
  • L’exigence d’alignement de l’élément est l’exigence d’alignement du type de base de l’élément. Notamment, cela signifie que l’exigence d’alignement pour un tableau char[20] est la même que celle requirejse pour un caractère simple.

Enfin, le besoin d’alignement de la structure dans son ensemble est le maximum des besoins d’alignement de chacun de ses éléments.

gcc insérera un remplissage après un élément donné pour s’assurer que le suivant (ou la structure si nous parlons du dernier élément) est correctement aligné. Il ne réorganisera jamais l’ordre des éléments dans la structure, même si cela permettra d’économiser de la mémoire.

Maintenant, les exigences d’alignement elles-mêmes sont également un peu bizarres.

  • Linux 32 bits nécessite que les types de données à 2 octets soient alignés sur 2 octets (leurs adresses doivent être paires). Tous les types de données plus importants doivent avoir un alignement de 4 octets (adresses se terminant par 0x0 , 0x4 , 0x8 ou 0xC ). Notez que cela s’applique également aux types de plus de 4 octets (tels que les double et long double ).
  • Windows 32 bits est plus ssortingct dans la mesure où si un type a une taille de K octets, il doit être aligné sur K octets. Cela signifie qu’un double ne peut être placé qu’à une adresse se terminant par 0x0 ou 0x8 . La seule exception à cela est le long double qui est toujours aligné sur 4 octets même s’il fait en fait 12 octets de long.
  • Pour Linux et Windows, sur les ordinateurs 64 bits, un type d’octet K doit être aligné sur les octets K. Encore une fois, le long double est une exception et doit être aligné sur 16 octets.

En supposant:

 sizeof(unsigned short) == 2 sizeof(int) == 4 

Ensuite, personnellement, j’utiliserais les éléments suivants (votre compilateur peut différer):

 unsigned shorts are aligned to 2 byte boundaries int will be aligned to 4 byte boundaries. typedef struct { unsigned short v1; // 0 bytes offset unsigned short v2; // 2 bytes offset unsigned short v3; // 4 bytes offset } myStruct; // End 6 bytes. // No part is required to align tighter than 2 bytes. // So whole structure can be 2 byte aligned. typedef struct { unsigned short v1; // 0 bytes offset unsigned short v2; // 2 bytes offset unsigned short v3; // 4 bytes offset /// Padding // 6-7 padding (so i is 4 byte aligned int i; // 8 bytes offset } myStruct; // End 12 bytes // Whole structure needs to be 4 byte aligned. // So that i is correctly aligned. 

Chaque type de données doit être aligné sur une limite de mémoire de sa propre taille. Ainsi, un short doit être aligné sur une limite de 2 octets et un int doit être sur une limite de 4 octets. De même, une long long devrait être sur une limite de 8 octets.

Dans votre première structure, chaque élément étant de taille short , la structure entière peut être alignée sur de short limites, il n’est donc pas nécessaire d’append de remplissage à la fin.

Dans la deuxième structure, l’int (probablement 32 bits) doit être aligné sur les mots pour qu’il insère un remplissage entre v3 et i pour aligner i .

La raison pour laquelle la deuxième sizeof(myStruct) est 12 est le remplissage inséré entre v3 et i pour aligner i sur une limite de 32 bits. Il y en a deux octets.

Wikipédia explique le remplissage et l’alignement de manière assez claire.

Le standard ne dit pas grand-chose sur la mise en page des structures avec des types complets – il s’agit du compilateur. Il a décidé qu’il avait besoin de l’int pour démarrer sur une frontière pour y accéder, mais comme il doit faire l’adressage de la mémoire sous-limite pour les courts-circuits, il n’est pas nécessaire de les remplir.

On dirait qu’il est aligné sur les limites en fonction de la taille de chaque var, de sorte que l’adresse est un multiple de la taille à laquelle on accède (les shorts sont alignés sur 2, les ints sont alignés sur 4, etc.) le int, sizeof(mystruct) devrait être 10. Bien sûr, tout dépend du compilateur utilisé et des parameters qu’il utilise à son tour.