( 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:
- Avant chaque membre individuel, il y aura un remplissage afin de le faire démarrer à une adresse divisible par sa taille.
par exemple sur un système 64 bits, int
doit commencer à l'adresse divisible par 4 et long
par 8 short
par 2.
char
et char[]
sont spéciaux, peuvent être n'importe quelle adresse mémoire, donc ils n'ont pas besoin de remplissage avant eux.
- Car
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:
- L'ordre des membres peut affecter la taille réelle de la structure, alors gardez cela à l'esprit. par exemple le
stu_c
et stu_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:
- L'
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 par 0
. )
Raison : le plus grand membre de structure individuel possible est de 16 octets ( long double
).
- (Mise à jour) Si une structure contient uniquement un
char
membre as, son adresse peut commencer à n'importe quelle adresse.
Espace vide :
- L'espace vide entre 2 structures pourrait être utilisé par des variables non-structures qui pourraient s'adapter. Par
exemple, en test_struct_address()
dessous, la variable x
réside entre la structure adjacente g
et h
.
Peu importe qu'il x
soit déclaré, h
l'adresse de ne changera pas, il x
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 :
/**
* Memory align & padding - for struct.
* compile: gcc memory_align.c
* execute: ./a.out
*/
#include <stdio.h>
// size is 8, 4 + 1, then round to multiple of 4 (int's size),
struct stu_a {
int i;
char c;
};
// size is 16, 8 + 1, then round to multiple of 8 (long's size),
struct stu_b {
long l;
char c;
};
// size is 24, l need padding by 4 before it, then round to multiple of 8 (long's size),
struct stu_c {
int i;
long l;
char c;
};
// size is 16, 8 + 4 + 1, then round to multiple of 8 (long's size),
struct stu_d {
long l;
int i;
char c;
};
// size is 16, 8 + 4 + 1, then round to multiple of 8 (double's size),
struct stu_e {
double d;
int i;
char c;
};
// size is 24, d need align to 8, then round to multiple of 8 (double's size),
struct stu_f {
int i;
double d;
char c;
};
// size is 4,
struct stu_g {
int i;
};
// size is 8,
struct stu_h {
long l;
};
// test - padding within a single struct,
int test_struct_padding() {
printf("%s: %ld\n", "stu_a", sizeof(struct stu_a));
printf("%s: %ld\n", "stu_b", sizeof(struct stu_b));
printf("%s: %ld\n", "stu_c", sizeof(struct stu_c));
printf("%s: %ld\n", "stu_d", sizeof(struct stu_d));
printf("%s: %ld\n", "stu_e", sizeof(struct stu_e));
printf("%s: %ld\n", "stu_f", sizeof(struct stu_f));
printf("%s: %ld\n", "stu_g", sizeof(struct stu_g));
printf("%s: %ld\n", "stu_h", sizeof(struct stu_h));
return 0;
}
// test - address of struct,
int test_struct_address() {
printf("%s: %ld\n", "stu_g", sizeof(struct stu_g));
printf("%s: %ld\n", "stu_h", sizeof(struct stu_h));
printf("%s: %ld\n", "stu_f", sizeof(struct stu_f));
struct stu_g g;
struct stu_h h;
struct stu_f f1;
struct stu_f f2;
int x = 1;
long y = 1;
printf("address of %s: %p\n", "g", &g);
printf("address of %s: %p\n", "h", &h);
printf("address of %s: %p\n", "f1", &f1);
printf("address of %s: %p\n", "f2", &f2);
printf("address of %s: %p\n", "x", &x);
printf("address of %s: %p\n", "y", &y);
// g is only 4 bytes itself, but distance to next struct is 16 bytes(on 64 bit system) or 8 bytes(on 32 bit system),
printf("space between %s and %s: %ld\n", "g", "h", (long)(&h) - (long)(&g));
// h is only 8 bytes itself, but distance to next struct is 16 bytes(on 64 bit system) or 8 bytes(on 32 bit system),
printf("space between %s and %s: %ld\n", "h", "f1", (long)(&f1) - (long)(&h));
// f1 is only 24 bytes itself, but distance to next struct is 32 bytes(on 64 bit system) or 24 bytes(on 32 bit system),
printf("space between %s and %s: %ld\n", "f1", "f2", (long)(&f2) - (long)(&f1));
// x is not a struct, and it reuse those empty space between struts, which exists due to padding, e.g between g & h,
printf("space between %s and %s: %ld\n", "x", "f2", (long)(&x) - (long)(&f2));
printf("space between %s and %s: %ld\n", "g", "x", (long)(&x) - (long)(&g));
// y is not a struct, and it reuse those empty space between struts, which exists due to padding, e.g between h & f1,
printf("space between %s and %s: %ld\n", "x", "y", (long)(&y) - (long)(&x));
printf("space between %s and %s: %ld\n", "h", "y", (long)(&y) - (long)(&h));
return 0;
}
int main(int argc, char * argv[]) {
test_struct_padding();
// test_struct_address();
return 0;
}
Résultat d'exécution - test_struct_padding()
:
stu_a: 8
stu_b: 16
stu_c: 24
stu_d: 16
stu_e: 16
stu_f: 24
stu_g: 4
stu_h: 8
Résultat d'exécution - test_struct_address()
:
stu_g: 4
stu_h: 8
stu_f: 24
address of g: 0x7fffd63a95d0 // struct variable - address dividable by 16,
address of h: 0x7fffd63a95e0 // struct variable - address dividable by 16,
address of f1: 0x7fffd63a95f0 // struct variable - address dividable by 16,
address of f2: 0x7fffd63a9610 // struct variable - address dividable by 16,
address of x: 0x7fffd63a95dc // non-struct variable - resides within the empty space between struct variable g & h.
address of y: 0x7fffd63a95e8 // non-struct variable - resides within the empty space between struct variable h & f1.
space between g and h: 16
space between h and f1: 16
space between f1 and f2: 32
space between x and f2: -52
space between g and x: 12
space between x and y: 12
space between h and y: 8
Ainsi, le début de l'adresse pour chaque variable est g: d0 x: dc h: e0 y: e8