Расширение структуры в C
Предположим, что определены две структуры, например:
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);
Ответы
В 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;
определенная в какой-либо другой единице перевода, не была изменена функцией.