Как скомпилировать код C с анонимными структурами / объединениями?

Я могу сделать это в c ++ / g ++:

struct vec3 { 
    union {
        struct {
            float x, y, z;
        }; 
        float xyz[3];
    }; 
};

Потом,

vec3 v;
assert(&v.xyz[0] == &v.x);
assert(&v.xyz[1] == &v.y);
assert(&v.xyz[2] == &v.z);

буду работать.

Как это сделать в c с помощью gcc? у меня есть

typedef struct {
    union {
        struct {
            float x, y, z;
        };
        float xyz[3];
    };
} Vector3;

Но у меня повсюду ошибки, в частности

line 5: warning: declaration does not declare anything
line 7: warning: declaration does not declare anything

person solinent    schedule 28.12.2009    source источник
comment
Снова скомпилируйте свой код с -Wall. GCC должен предупреждать о непереносимых анонимных структурах.   -  person greyfade    schedule 29.12.2009
comment
Даже в C ++ это очень плохая идея, и ее работа не гарантируется.   -  person sellibitze    schedule 29.12.2009
comment
Я не был уверен, где это разместить, но анонимные структуры и союзы являются частью стандарта C11. Поэтому, когда в комментариях ниже говорится, что это нестандартное расширение GNU, оно уже устарело.   -  person bk.    schedule 03.02.2012
comment
@sellibitze О чем ты говоришь? Это в принципе неплохая идея. Он просто создает объединение и помещает внутри объединения структуру и массив. Он хочет, чтобы они были анонимными, чтобы сократить продолжительность членского доступа.   -  person bobobobo    schedule 24.02.2012
comment
@solinent Всего 2 комментария: вам не нужна внешняя структура (вы должны написать union vec3 { ... }), и вы, вероятно, должны назвать член xyz чем-нибудь другим, кроме xyz. Что-то вроде e или comps работает нормально.   -  person bobobobo    schedule 24.02.2012
comment
@bobobobo Мне нравится думать о xyz как о swizzle-доступе, ala GLSL, даже если это не имеет большого смысла. Хотя, наверное, сейчас я бы использовал data. В первую очередь я использую C ++, поэтому я бы избегал всего этого и использовал функцию, которая в настоящее время возвращает ссылку на элемент данных и operator[] для индексированного доступа.   -  person solinent    schedule 28.02.2012


Ответы (10)


согласно http://gcc.gnu.org/onlinedocs/gcc/Unnamed-Fields.html#Unnamed-Fields

-fms-extensions включит функцию, которую хотите вы (и я).

person user287561    schedule 06.03.2010
comment
gcc 4.6 также включает эту функцию с -std=c1x и gcc 4.7+ с -std=c11 - person lambdapower; 21.03.2013

(Этот ответ относится к C99, а не к C11).

C99 не имеет анонимных структур или союзов. Вы должны назвать их:

typedef struct {
    union {
        struct {
            float x, y, z;
        } individual;
        float xyz[3];
    } data;
} Vector3;

И тогда вы должны использовать имя при доступе к ним:

assert(&v.data.xyz[0] == &v.data.individual.x);

В этом случае, поскольку ваша структура верхнего уровня имеет один элемент типа union, вы можете упростить это:

typedef union {
    struct {
        float x, y, z;
    } individual;
    float xyz[3];
} Vector3;

и доступ к данным теперь становится:

assert(&v.xyz[0] == &v.individual.x);
person R Samuel Klatchko    schedule 28.12.2009
comment
Есть ли специальное расширение gcc, которое я могу использовать? - person solinent; 29.12.2009
comment
GNU Dialect of C поддерживает анонимные структуры и союзы. - person David Grayson; 19.07.2010

Новый стандарт C11 будет поддерживать анонимные структуры и союзы, см. Предисловие, параграф 6 проекта от апреля 2011 года.

http://en.wikipedia.org/wiki/C1X

Странно то, что и gcc, и clang теперь поддерживают анонимные структуры и объединения в режимах C89 и C99. В моей машине никаких предупреждений не появляется.

person hdante    schedule 11.11.2011
comment
Однако флаг -pedantic это уловит. - person Norswap; 18.12.2013

Также всегда можно сделать следующее:

typedef struct
{
    float xyz[0];
    float x, y, z;
}Vec3;

Массив нулевой длины не выделяет никакой памяти, а просто сообщает C «указать на то, что будет объявлено следующим образом». Затем вы можете получить к нему доступ, как и к любому другому массиву:

int main(int argc, char** argv)
{
    Vec3 tVec;
    for(int i = 0; i < 3; ++i)
    {
        tVec.xyz[i] = (float)i;
    }

    printf("vec.x == %f\n", tVec.x);
    printf("vec.y == %f\n", tVec.y);
    printf("vec.z == %f\n", tVec.z);

    return 0;
}

Результат:

vec.x == 0.000000
vec.y == 1.000000
vec.z == 2.000000

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

person Ionoclast Brigham    schedule 10.07.2013
comment
Очень креативно, но мой компилятор (VS120) жалуется, что массивы нулевого размера тоже нестандартное расширение. Другой компилятор также должен предупреждать или не компилировать этот код. - person Johannes Jendersie; 03.07.2014
comment
Если вы компилируете с переключателем '-std = gnuXX' в gcc или clang, это не будет ошибкой или предупреждением, поскольку вы сообщаете компилятору, что признаете, что это расширение. Но да, в полностью совместимом со стандартами коде C я бы вместо этого просто использовал объединение. - person Ionoclast Brigham; 06.07.2014
comment
C99 поддерживает массивы переменного размера, поэтому в случае C99 (-std = c99) просто объявляйте как float [] и больше не нужно взламывать структуру. - person Alex; 19.10.2015
comment
На самом деле я соврал. Если вы измените float [0] (gnu99) на float [] (c99) - он не будет компилироваться, поскольку массив переменных ДОЛЖЕН находиться в конце структуры, что в данном случае не имеет никакого смысла. Так что float [0] это так. - person Alex; 27.11.2015

Анонимные союзы - это особенность языка C ++. В языке C нет анонимных объединений.

Анонимных структур не существует ни в C, ни в C ++.

Объявление, которое вы представили в своем вопросе, может быть скомпилировано с помощью компилятора GCC C ++, но это будет просто расширение для конкретного компилятора, которое не имеет ничего общего ни со стандартным C, ни со стандартным C ++.

Вдобавок ко всему, независимо от того, как вы это реализуете, ни C, ни C ++ не гарантируют, что ваши утверждения будут выполнены.

person AnT    schedule 28.12.2009
comment
в качестве примечания, gcc поддерживает это как расширение, хотя вы должны запускать gcc в нестандартном режиме C (по умолчанию) или явно использовать -std = gnu99 или аналогичный. - person nos; 29.12.2009
comment
Да, я знаю это и должен был упомянуть об этом. Это просто улучшает внешний вид кода, и его несложно исправить, если он был непереносимым. В данном случае я просто пишу его для себя, так что это не проблема. (Я пишу c raytracer, чтобы изучить тонкости c) - person solinent; 29.12.2009

Я могу сделать это в GCC без предупреждения

typedef union {
    struct { // human-friendly access
        float x;
        float y;
        float z;
        float w;
    };
    float xyz[3];
    struct { // human-friendly access
        float r;
        float g;
        float b;
        float a;
    };
    float rgb[3];
} Vector4f;

int main()
{
    Vector4f position, normal, color;
    // human-friendly access
    position.x = 12.3f;
    position.y = 2.f;
    position.z = 3.f;
    position.w = 1.f;

    normal.x = .8f;
    normal.y = .9f;
    normal.z = .1f;
    normal.w = 1.f;

    color.r = 1.f;
    color.g = .233f;
    color.b = 2.11f;
    color.a = 1.1f;

    // computer friendly access
    //some_processor_specific_operation(position.vec,normal.vec);
    return 0;
}

C: \> gcc vec.c -Стена

C: \> gcc --version gcc (GCC) 4.4.0 Авторские права (C) 2009 Free Software Foundation, Inc. Это бесплатное программное обеспечение; см. источник для условий копирования. Нет никаких гарантий; даже не для КОММЕРЧЕСКОЙ ЦЕННОСТИ или ПРИГОДНОСТИ ДЛЯ КОНКРЕТНОЙ ЦЕЛИ.

person Afriza N. Arief    schedule 29.01.2010
comment
По-прежнему использует расширения. Введите -pedantic в командной строке: main.cpp: 7: предупреждение: ISO C ++ запрещает анонимные структуры main.cpp: 14: предупреждение: ISO C ++ запрещает анонимные структуры - person GManNickG; 29.01.2010
comment
что ж, вопрос касается GCC, а не ISO C ++ ... хотя полезно знать, что говорит ISO C ++ ...; п - person Afriza N. Arief; 30.01.2010

Союзы Anonymouse не поддерживаются в C.

Также обратите внимание, что если вы объявите это так:

typedef struct {
    union {
        struct {
            float x, y, z;
        } individual;
        float xyz[3];
    } data;
} Vector3;

Делает

Vector3 v;
v.data.xyz[0] = 5;

float foo = v.data.individual.x;

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

В C вы предпочтете что-то вроде этого:

typedef struct {
    float v[3];
} Vec3;

И если вы не хотите использовать v [x], вы можете подумать:

#define X(V) ((V).v[0])

Vec3 v;
X(v) = 5.3;
printf("%f\n", X(v));
person Community    schedule 28.12.2009
comment
Стандарт гласит, что когда член объединения назначается, значение других членов не указано. В нем также говорится, что битовое представление распределяется между членами. Это не неопределенное поведение. Кажется, довольно четко определено. - person greyfade; 29.12.2009
comment
Стандарт говорит, что когда значение сохраняется в члене объекта типа union, байты представления объекта, которые не соответствуют этому члену, но соответствуют другим членам, принимают неопределенные значения. Но это не означает, что значение других членов не может быть ловушкой (их составные байты не являются просто). В нем говорится, что значение объекта структуры или объединения никогда не является представлением ловушки, даже если значение члена структуры или объекта объединения может быть представлением ловушки. Чтение из другого члена само по себе не является неопределенным поведением, но оно мая. - person Johannes Schaub - litb; 30.12.2009
comment
.. Это может быть, если мы читаем представление ловушки, а затем поведение не определено, как наиболее ясно сказано в сноске (ненормативной): если член, используемый для доступа к содержимому объекта union, не совпадает с последним членом используется для хранения значения в объекте, соответствующая часть объектного представления значения переинтерпретируется как представление объекта в новом типе, как описано в 6.2.6 (процесс, иногда называемый каламбуром типа). Это может быть изображение ловушки .. - person Johannes Schaub - litb; 30.12.2009

Диалект GNU языка C поддерживает анонимные структуры / объединения, но по умолчанию GCC компилируется с использованием некоего стандартного языка C. Чтобы использовать диалект GNU, введите «-std = gnu99» в командную строку.

person David Grayson    schedule 19.07.2010

Неопознанные члены структуры, не являющиеся стандартом ANSI / ISO C99, объясняют это, но я нахожу забавную вещь, когда на некоторых портах версий GNU C Compiler 2.xx использование неопознанных членов структуры работает, он их находит, не говорит что-то вроде " x не является членом union \ struct y, что такое x? », в других случаях это старый« x is undefined »,« x не является членом struct », черт возьми, я клянусь, что видел« указатель на неизвестное » "некоторое время назад, в связи с этим.

Поэтому я профессионально согласился бы со всеми остальными и просто дал бы элементу struct \ union идентификатор или, в случае UNION, тщательно переупорядочить код, чтобы объединение закончилось идентифицированным членом идентифицированной структуры и членами, которые были встроены в неопознанную структуру исходного союза, стали членами идентифицированной структуры и тщательно использовались с идентифицированным членом союза. Но в тех случаях, когда последний метод не был бы работоспособной заменой, я бы просто присвоил раздражающей структуре идентификатор и двинулся дальше.

person DLCJ    schedule 27.07.2010

Я могу предложить интересный обходной путь, чтобы избежать слишком большого количества полей в структуре. Рекомендуется предупреждать об определениях с простыми именами, так как это может вызвать конфликты.

#define x    ___fl_fld[0]
#define y    ___fl_fld[1]
#define z    ___fl_fld[2]
#define w    ___fl_fld[3]
#define r    ___fl_fld[0]
#define g    ___fl_fld[1]
#define b    ___fl_fld[2]
#define a    ___fl_fld[3]
typedef union {
    float ___fl_fld[4];
    float xyz[3];
    float rgb[3];
} Vector3;

Вы можете получить доступ к структуре следующим образом:

Vector3 v;
assert(&v.x == &v.r); //Should return true

В заключение, это будет мультитиповый союз, совместимый с C99:

#define u8llsb __u8[0]
#define u8lmsb __u8[1]
#define u8mlsb __u8[2]
#define u8mmsb __u8[3]
#define u16lsb __u16[0]
#define u16msb __u16[1]
#define u16    __u16[0]
#define u8lsb  __u8[0]
#define u8msb  __u8[1]

typedef union {
    uint32_t u32;
    int32_t  i32;
    uint16_t  __u16[2];
    uint8_t   __u8[4];
} multitype_t;

multitype_t Var;
var.u32;
var.i32;
var.u8llsb;
/* etc. */
person Felipe Lavratti    schedule 24.05.2012
comment
Я могу только представить себе удивительные ошибки компилятора, которые могут возникнуть после #define помещения всех ваших односимвольных имен переменных в __fl_fld[2]. - person StilesCrisis; 20.09.2014
comment
Бит для чтения для конкурса обфусцированного кода и способ для общих имен для определений в любых но записях конкурса обфусцированного кода ... - person fgp; 22.10.2014
comment
Вот почему многие стандарты кодирования не рекомендуют или запрещают использование макросов. Лично я считаю, что макросы препроцессора получают плохую репутацию. При разумном использовании они могут значительно улучшить качество кода. Но проблема здесь в том, что вы загрязняете глобальное пространство имен. Если кто-то включил файл заголовка, содержащий эти #definitions, и где-то у него была переменная с именем x или y и т. Д., Значит, вы нарушили его код. - person Die in Sente; 08.11.2016