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 devrait être de 4 octets. Dites 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 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és est de 12, et cette fois - sizeof(myStruct) == 12.

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

49
demandé sur Max Galkin 2011-03-25 20:14:31

10 réponses

Au moins sur la plupart des machines, un type n'est aligné que sur une limite aussi grande que le type lui-même [Edit: Vous ne pouvez pas vraiment exiger un alignement "plus" que cela, car vous devez pouvoir créer des tableaux, et vous ne pouvez 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. Puisque 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 est aligné sur une limite de 4 octets. Depuis il est précédé par 6 octets 2 octets de remplissage est inséré entre les v3 et i, donnant à 6 octets de données dans le shorts, deux octets de remplissage, et 4 octets de données dans le int, pour un total de 12.

45
répondu Jerry Coffin 2014-04-17 03:35:29

Oubliez d'avoir des membres différents, même si vous écrivez deux structures dont les membres sont exactement mêmes, 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, voir 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

C'est-à-dire que les tailles sont différentes même si les deux structures ont les mêmes membres!

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

La ligne du bas 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 n'importe quelle décision et vous ne pouvez pas supposer que tous les compilateurs prennent la même décision.

16
répondu Nawaz 2017-03-03 05:03:30

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

Dans votre exemple, 2 octets de remplissage sont ajoutés avant de i pour s'assurer que les 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, de sorte que votre structure sera alignée sur une limite de 4 octets.)

Le les règles réelles varient en fonction de la plate-forme-la page Wikipedia sur alignement de la structure de données a plus de détails.

Les compilateurs

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

13
répondu RichieHindle 2011-03-25 17:20:25

Tout d'abord, alors que les spécificités du remplissage sont laissées au compilateur, le système d'exploitation impose également des 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 et de 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.

Puis pour chaque entrée dans le struct:

  • l'espace minimum nécessaire 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 l'exigence d'une plaine char.

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érez remplissage après un élément donné pour s'assurer que la prochaine (ou la structure si nous parlons du dernier élément) est correctement aligné. Ilne réorganisera jamais 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 aussi un peu bizarres.

  • Linux 32 bits exige que les types de données de 2 octets aient un alignement de 2 octets (leurs adresses doivent être paires). Tous les types de données plus importants doivent avoir 4 octets l'alignement (adresses se terminant dans 0x0, 0x4, 0x8 ou 0xC). Notez que cela s'applique également aux types de plus de 4 octets (tels que double et long double).
  • Windows 32 bits est plus strict en ce sens que si un type a une taille de K octets, il doit être aligné sur K octets. Cela signifie qu'un double ne peut placés à une adresse se terminant en 0x0 ou 0x8. La seule exception à cela 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 64 bits machines, un type de K octet doit être aligné sur K octet. Encore une fois, le long double est une exception et doit être aligné sur 16 octets.
4
répondu Abhay Buch 2011-03-25 18:41:05

En supposant:

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

Alors personnellement, j'utiliserais ce qui suit (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.
4
répondu Martin York 2011-03-25 18:41:38

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

2
répondu Jonathan 2011-03-25 17:18:15

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

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

2
répondu Mark B 2011-03-25 17:20:00

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

Wikipedia explique le remplissage et l'alignement raisonnablement clairement.

1
répondu NPE 2011-03-25 17:17:33

La norme ne dit pas grand-chose sur la mise en page des structures avec des types complets - c'est jusqu'au compilateur. Il a décidé qu'il avait besoin de l'int pour commencer sur une limite pour y accéder, mais comme il doit faire l'adressage de la mémoire sous-limite pour les shorts, il n'est pas nécessaire de Les pad

0
répondu Martin Beckett 2011-03-25 17:18:51

On dirait qu'il est aligné sur les bounderies en fonction de la taille de chaque var, de sorte que l'adresse est un multiple de la taille accédée(donc les shorts sont alignés sur 2, les ints alignés sur 4, etc.), si vous avez déplacé l'un des shorts après l'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
répondu Necrolis 2011-03-25 17:21:05