( As respostas acima explicaram o motivo com bastante clareza, mas não parecem totalmente claras sobre o tamanho do preenchimento, portanto, adicionarei uma resposta de acordo com o que aprendi com The Lost Art of Structure Packing , que evoluiu para não se limitar a C
, mas também aplicável a Go
, Rust
. )
Alinhamento de memória (para struct)
Regras:
- Antes de cada membro individual, haverá preenchimento para que ele comece em um endereço divisível por seu tamanho.
por exemplo, no sistema de 64 bits, int
deve começar no endereço divisível por 4 e long
por 8, short
por 2.
char
e char[]
são especiais, podem ser qualquer endereço de memória; portanto, eles não precisam de preenchimento antes deles.
- Pois
struct
, além da necessidade de alinhamento de cada membro individual, o tamanho da estrutura inteira será alinhado com um tamanho divisível pelo tamanho do maior membro individual, preenchendo o final.
por exemplo, se o maior membro do struct for long
divisível por 8, int
depois por 4 e short
depois por 2.
Ordem do membro:
- A ordem do membro pode afetar o tamanho real da estrutura, portanto, lembre-se disso. por exemplo,
stu_c
e stu_d
do exemplo abaixo têm os mesmos membros, mas em ordem diferente e resultam em tamanho diferente para as 2 estruturas.
Endereço na memória (para struct)
Regras:
- Sistema de 64 bits O
endereço estrutural começa a partir de (n * 16)
bytes. ( Você pode ver no exemplo abaixo, todos os endereços hexadecimais impressos das estruturas terminam com 0
. )
Motivo : o maior membro individual possível da estrutura é de 16 bytes ( long double
).
- (Atualização) Se uma estrutura contiver apenas um
char
membro, seu endereço poderá começar em qualquer endereço.
Espaço vazio :
- O espaço vazio entre 2 estruturas poderia ser usado por variáveis não-estruturais que poderiam caber. Por
exemplo, test_struct_address()
abaixo, a variável x
reside entre estruturas g
e adjacentes h
.
Não importa se x
é declarado, h
o endereço do endereço não será alterado, x
apenas reutilize o espaço vazio que foi g
desperdiçado.
Caso semelhante para y
.
Exemplo
( para sistema de 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;
}
Resultado da execução - 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
Resultado da execução - 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
Assim, o início do endereço para cada variável é g: d0 x: dc h: e0 y: e8