Nie jest to zachowanie nieokreślone , niezależnie od tego, co mówi ktoś, urzędnik lub w inny sposób , ponieważ jest zdefiniowane przez standard. p->s
, z wyjątkiem sytuacji, gdy jest używany jako lwartość, jest obliczany na wskaźnik identyczny z (char *)p + offsetof(struct T, s)
. W szczególności jest to prawidłowy char
wskaźnik wewnątrz obiektu malloc, i istnieje 100 (lub więcej, zależnie od kwestii wyrównania) kolejnych adresów bezpośrednio po nim, które są również prawidłowe jako char
obiekty wewnątrz przydzielonego obiektu. Fakt, że wskaźnik został wyprowadzony przy użyciu ->
zamiast jawnego dodawania przesunięcia do wskaźnika zwróconego przez malloc
, rzutowany do char *
, jest nieistotny.
Technicznie rzecz biorąc, p->s[0]
jest to pojedynczy element char
tablicy wewnątrz struktury, kilka następnych elementów (np. p->s[1]
Through p->s[3]
) prawdopodobnie wypełnia bajty wewnątrz struktury, które mogą zostać uszkodzone, jeśli wykonasz przypisanie do struktury jako całości, ale nie jeśli uzyskasz dostęp tylko do poszczególnych członków, a pozostałe elementy to dodatkowe miejsce w przydzielonym obiekcie, z którego możesz dowolnie korzystać, o ile spełniasz wymagania dotyczące wyrównania (i char
nie masz żadnych wymagań dotyczących wyrównania).
Jeśli obawiasz się, że możliwość nakładania się na bajty wypełniające w strukturze może w jakiś sposób wywołać demony nosowe, możesz tego uniknąć, zastępując 1
in [1]
wartością, która zapewnia, że na końcu struktury nie ma wypełnienia. Prostym, ale marnotrawnym sposobem byłoby utworzenie struktury z identycznymi składowymi, z wyjątkiem braku tablicy na końcu, i użycie s[sizeof struct that_other_struct];
jej jako tablicy. Następnie p->s[i]
jest jasno zdefiniowany jako element tablicy w strukturze for i<sizeof struct that_other_struct
i jako obiekt char pod adresem następującym po końcu struktury for i>=sizeof struct that_other_struct
.
Edycja: W rzeczywistości, w powyższej sztuczce, aby uzyskać odpowiedni rozmiar, może być również konieczne umieszczenie unii zawierającej każdy prosty typ przed tablicą, aby upewnić się, że sama tablica zaczyna się od maksymalnego wyrównania, a nie w środku wypełnienia innego elementu . Ponownie, nie uważam, aby to wszystko było konieczne, ale oferuję to dla najbardziej paranoicznych prawników językowych.
Edycja 2: Nakładanie się bajtów wypełniających zdecydowanie nie stanowi problemu, ze względu na inną część standardu. C wymaga, aby jeśli dwie struktury zgadzały się w początkowym podciągu ich elementów, dostęp do wspólnych elementów początkowych można uzyskać za pomocą wskaźnika do dowolnego typu. W konsekwencji, gdyby struct T
zadeklarowano strukturę identyczną z, ale z większą tablicą końcową, element s[0]
musiałby pokrywać się z elementem s[0]
in struct T
, a obecność tych dodatkowych elementów nie mogłaby wpływać ani na nią nie wpływać dostęp do wspólnych elementów większej struktury używając wskaźnika do struct T
.