Considere a seguinte estrutura:
struct s {
int a, b;
};
Normalmente 1 , essa estrutura terá tamanho 8 e alinhamento 4.
E se criarmos dois struct s
objetos (mais precisamente, gravamos no armazenamento alocado dois desses objetos), com o segundo objeto sobreposto ao primeiro?
char *storage = malloc(3 * sizeof(struct s));
struct s *o1 = (struct s *)storage; // offset 0
struct s *o2 = (struct s *)(storage + alignof(struct s)); // offset 4
// now, o2 points half way into o1
*o1 = (struct s){1, 2};
*o2 = (struct s){3, 4};
printf("o2.a=%d\n", o2->a);
printf("o2.b=%d\n", o2->b);
printf("o1.a=%d\n", o1->a);
printf("o1.b=%d\n", o1->b);
Existe algo sobre esse comportamento indefinido do programa? Se sim, onde fica indefinido? Se não for UB, é garantido que você sempre imprima o seguinte:
o2.a=3
o2.b=4
o1.a=1
o1.b=3
Em particular, quero saber o que acontece com o objeto apontado por o1
quando o2
, que se sobrepõe, é gravado. Ainda é permitido acessar a parte desobstruída ( o1->a
)? O acesso à parte estragada o1->b
é o mesmo que o acesso o2->a
?
Como o tipo eficaz se aplica aqui? As regras são claras o suficiente quando você está falando sobre objetos e ponteiros que não se sobrepõem e apontam para o mesmo local da última loja, mas quando você começa a falar sobre o tipo eficaz de partes de objetos ou objetos sobrepostos, fica menos claro.
Alguma coisa mudaria se a segunda gravação fosse de um tipo diferente? Se os membros fossem dizer int
e em short
vez de dois int
s?
Aqui está um godbolt, se você quiser brincar com ele lá.
1 Esta resposta se aplica a plataformas onde também não é o caso: por exemplo, algumas podem ter tamanho 4 e alinhamento 2. Em uma plataforma em que o tamanho e o alinhamento eram iguais, essa pergunta não se aplicaria, pois objetos alinhados e sobrepostos ser impossível, mas não tenho certeza se existe alguma plataforma como essa.