В поисках ясности C Union - PullRequest
5 голосов
/ 20 мая 2010
typedef union {
    float flts[4];
    struct {
        GLfloat r;
        GLfloat theta;
        GLfloat phi;
        GLfloat w;
    };
    struct {
        GLfloat x;
        GLfloat y;
        GLfloat z;
        GLfloat w;
    };
} FltVector;

Хорошо, так что я думаю, что я понимаю, как использовать это (или, как я видел, как оно использовалось), т. Е.

FltVector fltVec1 = {{1.0f, 1.0f, 1.0f, 1.0f}};
float aaa = fltVec1.x;
etc.

Но я на самом деле не ворчу, сколько объёмов было объявлено профсоюзом (4 поплавка? 8 поплавков? 12 поплавков?), Как? и почему? Кроме того, почему два набора фигурных скобок при использовании FltVector {{}}?

Зачем вообще использовать союз? Почему бы не сделать ..

   struct FltVector {
        GLfloat x;
        GLfloat y;
        GLfloat z;
        GLfloat w;
   }

Любые указатели высоко ценятся (извините за каламбур)

Ответы [ 6 ]

5 голосов
/ 20 мая 2010

если sizeof(GLfloat) == sizeof(float), то было выделено 4 числа с плавающей запятой.

flts[0], r и x будут ссылаться здесь на один и тот же фрагмент памяти.

В объединении каждая переменная, объявленная в объединении, ссылается на один и тот же фрагмент памяти.

Здесь у нас есть 3 переменные, 2 структуры и массив, и каждая из них начинается в одной и той же точке памяти.

5 голосов
/ 20 мая 2010

Объединение позволяет вам «перерабатывать» одну и ту же область памяти для разных типов переменных. Обычно объединение занимает столько же памяти, сколько его единственный большой член, в данном случае, вероятно, 4 числа с плавающей запятой. Вы можете проверить с помощью sizeof.

В этом случае объединение, вероятно, используется для предоставления 1) альтернативных имен для тех же самых чисел в структуре (например, x и r совместно используют одну и ту же память), и 2) доступа к тому же четыре числа с плавающей запятой в виде массива (например, x и flts[0] совместно используют одну и ту же память). Иногда союзы используются в различных «взломах», обычно непереносимых, для доступа к внутренним компонентам некоторого типа данных, например отдельные байты в целом числе в машинном порядке.

2 голосов
/ 20 мая 2010

Несколько вопросов там:)

@ Арку прав насчет размера. Выравнивание также может сыграть свою роль, но, вероятно, не здесь.

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

Одна пара скобок предназначена для объединения, а другая для инициализатора массива.

1 голос
/ 20 мая 2010

В вашем примере, если мы рассмотрим имя переменных, объединение, скорее всего, не будет использоваться для доступа к одной и той же ячейке памяти, скажем, через x и r (поскольку радиус и координата x не подходят), но оставьте пользователя предоставить один и тот же аргумент для обоих. Намного проще установить x, y, z, w, когда вы используете декартовы координаты, и было бы неудобно использовать эти же имена для радиальных координат. И то, и другое проще, чем просто индексы массива. Возможно, у вас также есть другой параметр, который дает тип предоставленной координаты (декартовой или радиальной). Таким образом, у вас будет дискриминированный союз, как их называет pdbartlett.

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

коррекция : двойной уровень фигурных скобок позволяет избежать ввода входных данных в GLFloats.

Последняя деталь: неназванные внутренние структуры не являются стандартными C, стандартный способ сделать что-либо - дать имена внутренним структурам, как в

typedef union {
    float flts[4];
    struct {
        float r;
        float theta;
        float phi;
        float w;
    } cartesian;
    struct {
        float x;
        float y;
        float z;
        float w;
    } radial;
} FltVector;

FltVector f = {1.0, 2.0, 3.0, 4.0 };

int main(int argc, char * argv[]){
    printf("flts[0]=%f f.radial.r=%f f.cartesian.x=%f\n",
        f.flts[0], f.radial.r, f.cartesian.x);
}
1 голос
/ 20 мая 2010

почему два набора фигурных скобок при использовании FltVector {{}}

Посмотрите на всю строку, FltVector fltVec1 = {{1.0f, 1.0f, 1.0f, 1.0f}}; Вы инициализируете четыре числа с плавающей точкой в первой структуре в объединении. Как видно из жирного «in», есть два уровня вложенности. Если бы уровень вложенности был еще глубже, у вас могло бы быть даже больше фигурных скобок.

0 голосов
/ 20 мая 2010

Как уже было отмечено, код выделяет одну и ту же память, используя разные имена и типы данных. В некоторых случаях может быть удобно работать с именованными компонентами вектора (xyzw), но в то же время можно обрабатывать вектор как массив.

Похоже, что названия декартовых и радиальных структур меняются местами. «r», «theta» и «phi» являются общими именами радиальных координат, а не декартовыми, которые обычно обозначаются как «x», «y» и «z».

Я думаю, что стоит отметить, что использование разных представлений не является строго совместимым со стандартом (но, вероятно, прекрасно работает на всех существующих реализациях C) по двум причинам:

  1. Чтение члена объединения, которое не является последним написанным, дает неопределенный результат. Любая здравомыслящая реализация вернет значение, хранящееся в этой памяти.
  2. Компилятор может добавить заполнение между членами структуры (по соображениям производительности), в то время как массивы никогда не добавляются. Это вряд ли произойдет в этом случае на любом современном процессоре.
...