Союз структур с общим первым членом

Я не уверен, имеет ли код псевдоним указателя (или другие стандартные проблемы соответствия) в приведении утверждений. Кажется, что указатель на тип объединения должен быть в состоянии привести к указателю первого члена, и так как объединение состоит только из этих двух структур, я думаю, что приведение к первому члену должно работать, но я не конечно, если это правильно, или я замаскирую детали заполнения в процессе. Требуются ли профсоюзы для заполнения верхних битов?

Похоже, это неуточненное поведение? Есть ли у кого-нибудь понимание того, поддерживается ли это. Я знаю, что есть альтернативный стандартный способ сделать это с помощью структуры с enum type поле и struct container_storage член, но это кажется пустой тратой времени, учитывая, что эта информация уже struct contained

Команда компиляции в Linux: gcc -std=c99 -Wextra -pedantic -fstrict-aliasing test.c && ./a.out && echo $? возвращается 0

#include <stdlib.h>
#include <assert.h>

enum type {type_a = 1, type_b = 2};

struct contained {
    int some_other_field;
    enum type type;
};

struct container_a {
    struct contained contained;
    int test;
};


struct container_b {
    struct contained contained;
    char test;
};

union container_storage {
    struct container_a container_a;
    struct container_b container_b;
};

int
main(int argc, char **argv)
{
    union container_storage a =
        {.container_a = {.contained = {.type = type_a}, .test = 42}};
    union container_storage b =
        {.container_b = {.contained = {.type = type_b}, .test = 'b'}};

    assert(((struct contained *)&a)->type == type_a);
    assert(((struct contained *)&b)->type == type_b);

    return EXIT_SUCCESS;
}

Рекомендации:

[1] GCC, строгое псевдонимов и приведение через союз

[2] Что такое строгое правило наложения имен?

3 ответа

Решение

Это должно быть хорошо. C11, 6.5.2.3/6 ("Структура и члены профсоюза"):

Одна специальная гарантия сделана для того, чтобы упростить использование объединений: если объединение содержит несколько структур, которые имеют общую начальную последовательность (см. Ниже), и если объект объединения в настоящее время содержит одну из этих структур, разрешается проверять общие Начальная часть любого из них везде, где видна декларация о законченном типе объединения. Две структуры имеют общую начальную последовательность, если соответствующие элементы имеют совместимые типы (и, для битовых полей, одинаковой ширины) для последовательности из одного или нескольких начальных элементов.

(C++ дает ту же гарантию (C++11, 9.2/18) для союзов стандартной компоновки.)

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

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

В C99 добавлено дополнительное требование, согласно которому гарантии CIS применяются только тогда, когда виден полный тип объединения, содержащий обе структуры, что, по мнению некоторых авторов компилятора, означает, что оно применяется только к доступам, выполняемым непосредственно через типы объединения. Авторы таких компиляторов, похоже, считают, что функция должна обрабатывать функции с общим заголовком, например:

struct smallThing { void *next; uint16_t length; uint8_t dat[2]; };
struct bigThing { void *next; uint16_t length; uint8_t dat[65528]; };

должно быть, чтобы извлечь заголовок, как:

struct uHeader {  void *next; uint16_t length; };
struct smallThing { uHeader head; uint8_t dat[2]; };
struct bigThing { uHeader head; uint8_t dat[15994]; };

или используйте объекты типа union для всего, даже если использование uHeader увеличит размер struct smallThing на 50% (и полностью сломать любой код, который зависел от его компоновки), и использование объединений для всего, когда большинству объектов требуется только небольшой размер, увеличило бы использование памяти в тысячу раз.

Если нужно, чтобы код был совместим с компиляторами, которые по существу игнорируют правило Common Initial Sequence, следует рассматривать правило Common Initial Sequence как по существу бесполезное. Лично я думаю, что было бы лучше задокументировать, что только компиляторы, которые соблюдают CIS, должны считаться подходящими для использования со своим кодом, а не отклоняться назад для размещения неподходящих компиляторов, но я думаю, что важно знать, что компиляторы, подобные последнему те существуют.

Насколько я могу судить, clang и gcc не соблюдают правило CIS каким-либо полезным способом, кроме случаев, когда -fno-strict-aliasing флаг установлен. Я не знаю о других компиляторах.

Другие вопросы по тегам