Rozszerzenie Struct w C

Aug 21 2020

Załóżmy, że istnieją dwie zdefiniowane reklamy, takie jak:

typedef struct {
   T x;
   T y;
} A;

typedef struct {
   A a;
   T z;
} B;

Czy mogę traktować wskaźnik do struktury B jako wskaźnik do struktury A?

W praktyce jest to niezawodny / standardowy / przenośny / niezmienny przez kompilator:

B b = {{1,2},3};
A * a = &b;

print(a->x);
print(a->y);

Odpowiedzi

3 Lundin Aug 21 2020 at 08:19

C17 6.7.2.1 stwierdza to (wyróżnienie moje):

W obrębie obiektu strukturalnego elementy składowe niebędące polami bitowymi i jednostki, w których znajdują się pola bitowe, mają adresy, które rosną w kolejności, w jakiej są deklarowane. Wskaźnik do obiektu struktury, odpowiednio przekonwertowany, wskazuje na jego początkowy element członkowski (lub jeśli ten element jest polem bitowym, to na jednostkę, w której się znajduje) i odwrotnie .

Oznacza to, że musisz „odpowiednio przekonwertować” wskaźnik B bobiektu na typ jego pierwszego elementu. Ta konwersja nie zachodzi niejawnie, musisz to zrobić za pomocą jawnego rzutowania:

A * a = (A*)&b;

Takie postępowanie jest dobrze zdefiniowane i bezpieczne, zgodnie z cytowaną częścią powyżej.

Podobnie, kompilator nie może zakładać, że wskaźnik do Ai wskaźnik do Bnie mają aliasu. Reguła efektywnego typu 6.5.7 („ścisłe aliasing”) stanowi wyjątek w tym przypadku:

Obiekt ma dostęp do swojej przechowywanej wartości tylko za pomocą wyrażenia l-wartości, które ma jeden z następujących typów:
...

  • agregat lub typ związku, który obejmuje jeden z wyżej wymienionych typów wśród swoich członków

Na przykład podczas optymalizacji kompilator wywołujący funkcję void func (B* b)nie może założyć, że zewnętrzna zmienna liniowa extern A a;zdefiniowana w innej jednostce translacji nie została zmieniona przez funkcję.