Работая с 3D-точками, я столкнулся с таким подходом к определению типа:
union point_3d {
struct {
GLdouble x, y, z;
} coord;
GLdouble tab[ 3 ];
};
Таким образом, к координатам можно получить доступ как по имени, так и по индексу, используя, например, p.coord.x и p.tab.[0] взаимозаменяемо.
Это более полезно, чем просто typedef GLdouble point_3d[3];, поскольку объединения поддерживают прямое присваивание (и, конечно, мы можем получить доступ к координатам по имени), но также более полезно, чем struct point_3d { glDouble x, y, z; };, поскольку тип можно итерировать напрямую без использования sizeof.
Но на самом деле важный вопрос: безопасно ли это? Это портативное? Я не смог найти ничего, что могло бы предположить, что это не так, но я не смог подтвердить, что это гарантировано, поэтому я спрашиваю здесь людей, надеюсь, более знающих, чем я.
(хотя мне интересно узнать, работает ли это в Windows, я конкретно имею в виду переносимость POSIX. Windows имеет для меня лишь второстепенное значение)





C позволяет каламбур через союзы.
Это означает, что вы можете писать в один член объединения (например, в структуру) и читать из другого члена (массива). И наоборот, конечно.
Большая возможная проблема заключается в том, содержит ли структура какие-либо отступы между элементами или нет. Если да, то он не соответствует массиву. Хотя в данном случае это маловероятно.
@TedLyngmo Это слишком строгий тест, поскольку заполнение после членов coord и tab не важно, если оно пробралось. Что-то вроде static_assert(sizeof(point_3d.coord) == sizeof(point_3d.tab),""); - лучший тест.
@chux-ReinstateMonica Действительно ли разрешено добавлять поля вокруг массива? Я думал, что размер union foo { type x[X]; } должен быть таким же, как sizeof(type[X]).
@TedLyngmo, рассмотрим union foo { char bar[3]; struct baz { char a,b,c; }; short whiz;}; с потенциальным размером 4 (1 блокнот после .bar/.baz. Размер .bar и .baz равен 3.
@chux-ReinstateMonica Правда, это испортило бы день :) Я думал о случаях, когда члены перекрываются 1:1 и имеют одинаковое выравнивание, но, конечно, если другие участники будут с этим возиться, это не сработает .
@TedLyngmo Тот факт, что третий участник может стать катализатором заполнения и, таким образом, демонстрирует, что дополнение возможно в union. Тем не менее, даже для 2: char [3] и char a,b,c нет спецификации, которая не требовала бы заполнения, поэтому заполнение возможно, даже если оно маловероятно. Например, union могут жить в стране с четными адресами.
Стандарт C в интерпретации clang и gcc определяет поведение только тогда, когда lvalue имеет форму unionLvalue.member. Даже значения l формы *(unionLvalue.arrayMember+index) не будут последовательно распознаваться как способные идентифицировать то же хранилище, что и другие члены объединения. Я бы не стал доверять clang или gcc для надежной обработки такого кода без использования -fno-strict-aliasing.
Я думаю, можно использовать
assert, чтобы убедиться, что никакие отступы не пробрались. В C23static_assert(sizeof(union point_3d) == sizeof(GLdouble[3]));должно быть достаточно.