Расширение структуры в C

Aug 21 2020

Предположим, что определены две структуры, например:

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

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

Могу ли я рассматривать указатель на структуру B как указатель на структуру A?

На практике это надежный / стандартный / переносимый / инвариантный к компилятору:

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

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

Ответы

3 Lundin Aug 21 2020 at 08:19

В C17 6.7.2.1 говорится об этом (выделено мной):

Внутри объекта структуры небитовые поля и блоки, в которых находятся битовые поля, имеют адреса, возрастающие в том порядке, в котором они объявлены. Указатель на объект структуры, преобразованный соответствующим образом, указывает на его начальный член (или, если этот член является битовым полем, то на модуль, в котором он находится), и наоборот .

Это означает, что вы должны «надлежащим образом преобразовать» указатель B bобъекта в тип его первого члена. Это преобразование не происходит неявно, вы должны сделать это с помощью явного приведения:

A * a = (A*)&b;

Это четко определено и безопасно, как указано в приведенной выше части.

Точно так же компилятору не разрешается предполагать, что указатель на Aи указатель на Bне являются псевдонимами. Правило эффективного типа 6.5.7 («строгое алиасинг») дает исключение для этого случая:

Сохраненное значение объекта должно быть доступно только выражению lvalue, которое имеет один из следующих типов:
...

  • тип агрегата или объединения, который включает в себя один из вышеупомянутых типов среди своих членов

Например, во время оптимизации компилятору, вызывающему функцию void func (B* b), не разрешается предполагать, что внешняя переменная linage, extern A a;определенная в какой-либо другой единице перевода, не была изменена функцией.