Considérer:
struct mystruct_A
{
char a;
int b;
char c;
} x;
struct mystruct_B
{
int b;
char a;
} y;
Les tailles des structures sont respectivement 12 et 8.
Ces structures sont-elles rembourrées ou emballées?
Quand le rembourrage ou l'emballage a-t-il lieu?
padding
rend les choses plus grandes.packing
rend les choses plus petites. Totalement différent.Réponses:
Le remplissage aligne les membres de la structure sur les limites d'adresse "naturelles" - par exemple, les
int
membres auraient des décalages, qui sontmod(4) == 0
sur une plate-forme 32 bits. Le remplissage est activé par défaut. Il insère les "lacunes" suivantes dans votre première structure:L'emballage , d'autre part, empêche le compilateur de faire du remplissage - cela doit être explicitement demandé - sous GCC c'est
__attribute__((__packed__))
, donc ce qui suit:produirait une structure de taille
6
sur une architecture 32 bits.Remarque cependant: l'accès à la mémoire non alignée est plus lent sur les architectures qui le permettent (comme x86 et amd64), et est explicitement interdit sur les architectures à alignement strict comme SPARC.
la source
( Les réponses ci-dessus expliquent la raison assez clairement, mais ne semblent pas totalement claires sur la taille du rembourrage, donc, j'ajouterai une réponse en fonction de ce que j'ai appris de The Lost Art of Structure Packing , il a évolué pour ne pas se limiter à
C
, mais également applicable àGo
,Rust
. )Alignement de la mémoire (pour struct)
Règles:
par exemple sur un système 64 bits,
int
doit commencer à l'adresse divisible par 4 etlong
par 8short
par 2.char
etchar[]
sont spéciaux, peuvent être n'importe quelle adresse mémoire, donc ils n'ont pas besoin de remplissage avant eux.struct
, à part le besoin d'alignement pour chaque membre individuel, la taille de la structure entière elle-même sera alignée sur une taille divisible par la taille du plus grand membre individuel, en remplissant à la fin.Par exemple, si le plus grand membre de struct est
long
alors divisible par 8,int
puis par 4,short
puis par 2.Ordre des membres:
stu_c
etstu_d
de l' exemple ci - dessous ont les mêmes membres, mais dans un ordre différent, et le résultat de différentes tailles pour les 2 struct.Adresse en mémoire (pour struct)
Règles:
adresse Struct du système 64 bits commence à partir des
(n * 16)
octets. ( Vous pouvez voir dans l'exemple ci-dessous que toutes les adresses hexadécimales imprimées des structures se terminent par0
. )Raison : le plus grand membre de structure individuel possible est de 16 octets (
long double
).char
membre as, son adresse peut commencer à n'importe quelle adresse.Espace vide :
exemple, en
test_struct_address()
dessous, la variablex
réside entre la structure adjacenteg
eth
.Peu importe qu'il
x
soit déclaré,h
l'adresse de ne changera pas, ilx
suffit de réutiliser l'espace vide qui a étég
gaspillé.Cas similaire pour
y
.Exemple
( pour système 64 bits )
memory_align.c :
Résultat d'exécution -
test_struct_padding()
:Résultat d'exécution -
test_struct_address()
:Ainsi, le début de l'adresse pour chaque variable est g: d0 x: dc h: e0 y: e8
la source
<The Lost Art of C Structure Packing>
explique assez bien les règles, même s'il est un peu plus long que cette réponse. Le livre est disponible gratuitement en ligne: catb.org/esr/structure-packingJe sais que cette question est ancienne et la plupart des réponses ici expliquent très bien le rembourrage, mais en essayant de le comprendre moi-même, je me suis dit que le fait d'avoir une image "visuelle" de ce qui se passait aidait.
Le processeur lit la mémoire en "morceaux" d'une taille définie (mot). Disons que le mot du processeur fait 8 octets. Il considérera la mémoire comme une grande rangée de blocs de construction de 8 octets. Chaque fois qu'il a besoin d'obtenir des informations de la mémoire, il atteint l'un de ces blocs et l'obtient.
Comme le montre l'image ci-dessus, peu importe où se trouve un caractère (1 octet de long), car il se trouvera dans l'un de ces blocs, nécessitant que le processeur ne traite qu'un seul mot.
Lorsque nous traitons des données supérieures à un octet, comme un entier de 4 octets ou un double de 8 octets, la façon dont elles sont alignées dans la mémoire fait une différence sur le nombre de mots qui devront être traités par le CPU. Si les blocs de 4 octets sont alignés de manière à ce qu'ils s'adaptent toujours à l'intérieur d'un bloc (l'adresse mémoire étant un multiple de 4), un seul mot devra être traité. Sinon, un bloc de 4 octets pourrait avoir une partie de lui-même sur un bloc et une partie sur un autre, nécessitant que le processeur traite 2 mots pour lire ces données.
La même chose s'applique à un double de 8 octets, sauf que maintenant il doit être dans un multiple d'adresse mémoire de 8 pour garantir qu'il sera toujours à l'intérieur d'un bloc.
Cela considère un traitement de texte à 8 octets, mais le concept s'applique à d'autres tailles de mots.
Le remplissage fonctionne en comblant les écarts entre ces données pour s'assurer qu'elles sont alignées avec ces blocs, améliorant ainsi les performances lors de la lecture de la mémoire.
Cependant, comme indiqué dans d'autres réponses, parfois l'espace compte plus que la performance elle-même. Peut-être que vous traitez beaucoup de données sur un ordinateur qui n'a pas beaucoup de RAM (l'espace de swap pourrait être utilisé mais il est BEAUCOUP plus lent). Vous pouvez organiser les variables dans le programme jusqu'à ce que le moins de remplissage soit effectué (comme cela a été largement illustré dans certaines autres réponses), mais si cela ne suffit pas, vous pouvez explicitement désactiver le remplissage, ce qui est ce qu'est l' emballage .
la source
L'emballage de structure supprime le rembourrage de structure, le rembourrage utilisé lorsque l'alignement est le plus important, l'emballage utilisé lorsque l'espace est le plus important.
Certains compilateurs prévoient
#pragma
de supprimer le remplissage ou de le compresser à n nombre d'octets. Certains fournissent des mots clés pour ce faire. Généralement, le pragma utilisé pour modifier le remplissage de la structure sera au format ci-dessous (dépend du compilateur):Par exemple, ARM fournit le
__packed
mot clé pour supprimer le remplissage de la structure. Parcourez le manuel de votre compilateur pour en savoir plus à ce sujet.Une structure compacte est donc une structure sans rembourrage.
Des structures généralement tassées seront utilisées
pour économiser de l'espace
formater une structure de données à transmettre sur le réseau en utilisant un certain protocole (ce n'est pas une bonne pratique bien sûr car vous devez
gérer l'endianité)
la source
Le rembourrage et l'emballage ne sont que deux aspects de la même chose:
Dans
mystruct_A
, en supposant un alignement par défaut de 4, chaque membre est aligné sur un multiple de 4 octets. Puisque la taille dechar
est 1, le remplissage poura
etc
est 4 - 1 = 3 octets alors qu'aucun remplissage n'est requis pourint b
ce qui est déjà 4 octets. Il en va de même pourmystruct_B
.la source
L'empaquetage de structure n'est effectué que lorsque vous demandez explicitement à votre compilateur d'emballer la structure. Le rembourrage est ce que vous voyez. Votre système 32 bits remplit chaque champ pour l'alignement des mots. Si vous aviez dit à votre compilateur de compresser les structures, elles auraient respectivement 6 et 5 octets. Mais ne fais pas ça. Ce n'est pas portable et fait que les compilateurs génèrent du code beaucoup plus lent (et parfois même bogué).
la source
Il n'y a pas de mais! Qui veut saisir le sujet doit faire les suivants,
la source
Règles de rembourrage:
Pourquoi la règle 2: Considérez la structure suivante,
Si nous devions créer un tableau (de 2 structures) de cette structure, aucun remplissage ne sera requis à la fin:
Par conséquent, la taille de struct = 8 octets
Supposons que nous devions créer une autre structure comme ci-dessous:
Si nous devions créer un tableau de cette structure, il y a 2 possibilités, du nombre d'octets de remplissage requis à la fin.
A. Si nous ajoutons 3 octets à la fin et l'alignons pour int et non pour Long:
B. Si nous ajoutons 7 octets à la fin et l'alignons pour Long:
L'adresse de début du deuxième tableau est un multiple de 8 (c'est-à-dire 24). La taille de la structure = 24 octets
Par conséquent, en alignant l'adresse de début du tableau suivant de la structure sur un multiple du plus grand membre (c'est-à-dire si nous devions créer un tableau de cette structure, la première adresse du deuxième tableau doit commencer à une adresse qui est un multiple du plus grand membre de la structure. Ici, 24 (3 * 8)), nous pouvons calculer le nombre d'octets de remplissage requis à la fin.
la source
L'alignement de la structure des données est la façon dont les données sont organisées et accessibles dans la mémoire de l'ordinateur. Il se compose de deux problèmes distincts mais liés: l'alignement des données et le remplissage de la structure des données . Lorsqu'un ordinateur moderne lit ou écrit dans une adresse mémoire, il le fait en blocs de taille de mot (par exemple, blocs de 4 octets sur un système 32 bits) ou plus. L'alignement des données signifie placer les données à une adresse mémoire égale à un multiple de la taille du mot, ce qui augmente les performances du système en raison de la façon dont le CPU 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 est le remplissage de la structure de données.
la source