Rozważ następującą strukturę:
struct s {
int a, b;
};
Zazwyczaj 1 , ta struktura będzie miała rozmiar 8 i wyrównanie 4.
Co jeśli utworzymy dwa struct s
obiekty (a ściślej zapisujemy do przydzielonej pamięci dwa takie obiekty), przy czym drugi obiekt nakłada się na pierwszy?
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);
Czy coś w tym programie jest niezdefiniowane? Jeśli tak, to gdzie staje się niezdefiniowane? Jeśli nie jest to UB, to zawsze gwarantuje wydruk następujących informacji:
o2.a=3
o2.b=4
o1.a=1
o1.b=3
W szczególności chcę wiedzieć, co dzieje się z obiektem wskazywanym przez o1
moment o2
, w którym nakłada się on na siebie. Czy nadal można uzyskiwać dostęp do niezakleszczonej części ( o1->a
)? Czy dostęp do części zablokowanej jest o1->b
po prostu taki sam jak dostęp o2->a
?
W jaki sposób stosuje się tutaj typ skuteczny ? Reguły są wystarczająco jasne, gdy mówimy o nie nakładających się obiektach i wskaźnikach, które wskazują na to samo miejsce co ostatni sklep, ale kiedy zaczynasz mówić o efektywnym typie części obiektów lub nakładających się obiektów, jest to mniej jasne.
Czy cokolwiek by się zmieniło, gdyby drugi zapis był innego typu? Jeżeli członkowie byli powiedzieć, int
a short
raczej niż dwa int
s?
Oto godbolt, jeśli chcesz się z nim pobawić.
1 Ta odpowiedź dotyczy również platform, na których tak nie jest: np. Niektóre mogą mieć rozmiar 4 i wyrównanie 2. Na platformie, w której rozmiar i wyrównanie były takie same, pytanie nie miałoby zastosowania, ponieważ wyrównane, nakładające się obiekty być niemożliwe, ale nie jestem pewien, czy istnieje taka platforma.