Союз структур с общим первым членом
Я не уверен, имеет ли код псевдоним указателя (или другие стандартные проблемы соответствия) в приведении утверждений. Кажется, что указатель на тип объединения должен быть в состоянии привести к указателю первого члена, и так как объединение состоит только из этих двух структур, я думаю, что приведение к первому члену должно работать, но я не конечно, если это правильно, или я замаскирую детали заполнения в процессе. Требуются ли профсоюзы для заполнения верхних битов?
Похоже, это неуточненное поведение? Есть ли у кого-нибудь понимание того, поддерживается ли это. Я знаю, что есть альтернативный стандартный способ сделать это с помощью структуры с 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;
}
Рекомендации:
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
флаг установлен. Я не знаю о других компиляторах.