web-dev-qa-db-fra.com

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. Dis 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 doit ê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 de 12, et cette fois sizeof(myStruct) == 12.

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

53
Ivan

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

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

Le second contient un élément de 4 octets, qui s'aligne 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, donnant 6 octets de données dans les shorts, deux octets de remplissage et 4 autres octets de données dans les int pour un total de 12.

50
Jerry Coffin

Oubliez d'avoir des membres différents, même si vous écrivez deux structures dont les membres sont exactement identiques, avec une différence est que l'ordre dans lequel ils sont déclarés est différent, alors la taille de chaque structure peut être (et est souvent) différente.

Par exemple, voyez ceci,

#include <iostream>
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;
}

Compilez-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 la norme ne parle pas de la façon dont le remplissage doit être fait, et donc les compilateurs sont libres de prendre toute décision et vous ne pouvez pas assumer tout les compilateurs prennent la même décision.

19
Nawaz

Par défaut, les valeurs sont alignées en fonction de leur taille. Ainsi, une valeur de 2 octets comme un short est alignée sur une frontière de 2 octets, et une valeur de 4 octets comme un int est alignée sur une frontière 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 frontière au moins aussi grande que la plus grande valeur de la structure, donc votre structure sera alignée sur une frontière de 4 octets.)

Les règles réelles varient selon la plate-forme - la page Wikipedia sur Alignement de la structure des données a plus de détails.

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

13
RichieHindle

En supposant:

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

Ensuite, j'utiliserais personnellement 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.
5
Martin York

Premièrement, alors que les spécificités du remplissage sont laissées au compilateur, le système d'exploitation impose également quelques règles quant aux exigences 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, supposez que la structure commence toujours à une adresse correctement alignée pour les types de données all.

Ensuite, pour chaque entrée de la structure:

  • L'espace minimum requis 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. Cela signifie notamment que l'exigence d'alignement pour un char[20] array est le même que l'exigence d'un char simple.

Enfin, l'exigence d'alignement de la structure dans son ensemble est le maximum des exigences 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é. Cela jamais réorganisera l'ordre des éléments dans la structure, même si cela économisera 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 aient un alignement à 2 octets (leurs adresses doivent être paires). Tous les types de données plus volumineux doivent avoir un alignement sur 4 octets (les adresses se terminant par 0x0, 0x4, 0x8 ou 0xC). Notez que cela s'applique également aux types supérieurs à 4 octets (tels que double et long double).
  • Windows 32 bits est plus strict car 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 à cette règle est le long double qui est toujours aligné sur 4 octets même s'il est en fait long de 12 octets.
  • Pour Linux et Windows, sur les machines 64 bits, un type K octet doit être aligné K octet. Encore une fois, le long double est une exception et doit être aligné sur 16 octets.
4
Abhay Buch

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

Dans la deuxième structure, l'int (probablement 32 bits) doit être aligné sur Word afin d'insérer un remplissage entre v3 et i pour aligner i.

2
Mark B

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

2
Jonathan

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

Wikipedia explique assez clairement le rembourrage et l'alignement.

1
NPE

Cela ressemble à son alignement sur les limites en fonction de la taille de chaque var, de sorte que l'adresse soit un multiple de la taille à laquelle vous accédez (donc les courts métrages sont alignés sur 2, les entiers alignés sur 4, etc.), si vous avez déplacé l'un des courts métrages après int, sizeof(mystruct) devrait être 10. Bien sûr, tout dépend du compilateur utilisé et des paramètres qu'il utilise à son tour.

0
Necrolis

La norme ne dit pas grand-chose sur la disposition des structures avec des types complets - c'est jusqu'à le 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 un adressage de mémoire sous-frontière pour les courts-circuits, il n'est pas nécessaire de les remplir.

0
Martin Beckett