Соответствует ли стандарту совмещение указателей между агрегированными структурами C и их членами?

Является ли следующая программа C соответствующей стандартам (и по каким стандартам)? Если он не соответствует требованиям (нарушая строгое правило псевдонимов или иным образом), является ли он «безопасным» по отношению к GCC или любому другому широко используемому компилятору C?

int main()
{
    typedef struct {
       int data;
    } Type1;

    typedef struct {
       float data;
    } Type2;

    typedef struct {
       Type1 type1;
       Type2 type2;
    } Aggregate;

    Aggregate aggregate;
    aggregate.type1.data = 1;
    aggregate.type2.data = 2.0;

    Aggregate *p_aggegregate = &aggregate;
    Type1     *p_type1       = (Type1*) &aggregate;
    Type2     *p_type2       = (Type2*) &aggregate;

    int   data1 = p_type1->data;
    float data2 = p_type2->data;

    return 0;
}

person Patrick Sanan    schedule 19.08.2016    source источник
comment
p_type2 не указывает на объект типа Type2, как вы предлагаете использовать этот указатель? Или вы не собираетесь его использовать?   -  person 2501    schedule 19.08.2016
comment
Простое указание указателя на объект не дает к нему доступа. Так что псевдоним не является проблемой.   -  person 2501    schedule 19.08.2016
comment
Type1 *p_type1 = &aggregate и вариант Type2 являются несовместимым преобразованием указателя.   -  person ensc    schedule 19.08.2016
comment
Строгое правило алиасинга (C11 6.5 §7) имеет значение только тогда, когда вы действительно начинаете повторно ссылаться на сохраненные значения объектов. Этот код содержит только различные недопустимые преобразования указателей, он имеет мало общего со строгим псевдонимом.   -  person Lundin    schedule 19.08.2016
comment
Спасибо за комментарии - код был обновлен для приведения указателей и их разыменования.   -  person Patrick Sanan    schedule 19.08.2016


Ответы (1)


Строго говоря, ваша программа соответствует стандартам (C11, C99, не знаю насчет K&R). Он содержит только преобразования указателей, и в стандарте C очень мало правил, которые его ограничивают

Ваш

Type1     *p_type1       = &aggregate;
Type2     *p_type2       = &aggregate;

нарушает безопасность типов, и, вероятно, каждый компилятор предупредит об этом как о недопустимом преобразовании типов. Но это покрывается

7 Указатель на тип объекта может быть преобразован в указатель на другой тип объекта. [6.3.2.3 «Указатели», проект C11]

Я не нашел правила, требующего явного приведения

Type1     *p_type1       = (Type1 *)&aggregate;

и абзац выше использует аналогичную формулировку, как и абзац 1, который допускает часто используемое неявное преобразование something в/из void *.

Доступ к обеим переменным действителен, поскольку &aggregate == &aggregate.type1.

Но, как уже говорилось, это действительно плохой и небезопасный стиль написания вещей таким образом, и обычные стили кодирования будут рассматривать это как ошибку. Доступ будет неопределенным, когда вы добавите, например. атрибут char foo; перед type1.

РЕДАКТИРОВАТЬ:

Программа недействительна; это нарушает

- левый операнд имеет атомарный, квалифицированный или неквалифицированный тип указателя, и [...] оба операнда являются указателями на уточненные или неквалифицированные версии совместимых типов [...] [6.5.16.1 "Простое назначение"]

с

2 Чтобы два типа указателей были совместимы, оба должны быть одинаково квалифицированы и оба должны быть указателями на совместимые типы. [6.7.6.1 "Деклараторы указателей"]

person ensc    schedule 19.08.2016
comment
Благодарность; исправил пункт. Но расклад может измениться. Стандарт C не определяет, как должна быть выровнена структура. Могут быть ABI, зависящие от архитектуры, которые требуют, чтобы структура была выровнена, как их наиболее выровненный атрибут. Но этого не требует С. - person ensc; 19.08.2016
comment
Некоторые ABI требуют этого, но не C. - person ensc; 19.08.2016
comment
Если указатели не указывают на совместимые типы, то для принудительного преобразования требуется явное приведение типов. Этого требуют правила простого присвоения 6.5.16.1. Квалификаторы также должны совпадать. Соответствующая секция левого операнда имеет атомарный, квалифицированный или неквалифицированный тип указателя, и (учитывая тип, который будет иметь левый операнд после преобразования lvalue) оба операнда являются указателями на уточненные или неполные версии совместимых типов, и тип, на который указывает тип left имеет все квалификаторы типа, на который указывает right; - person Lundin; 19.08.2016