У меня есть следующая структура союза и структур
union ab {
struct {
int a;
} a_st;
struct {
int b;
} b_st;
};
typedef struct c {
union ab;
} c_st;
при попытке "достучаться" до элементов объединения напрямую:
c_st c;
printf("%d\n", c.a_st.a);
возникла следующая ошибка компиляции:
ошибка: 'c_st' {иначе 'struct c'} не имеет члена с именем 'a_st'
если я укажу имя союза внутри структуры c_st (например, ab_un), это сработает, но тогда мне нужно вызвать c.ab_un.a_st.a
, что менее желательно.
это обязательно зло или я что-то здесь пропустил?
заранее спасибо
struct c { union ab; }
Вам не приходит предупреждение?
@Jean-ClaudeArbaut - выбранный ответ из stackoverflow.com/questions/1972003/… сработал, спасибо!
К вашему сведению, стандарт C 2018 определяет анонимные члены в 6.7.2.1 13. В нем говорится, что анонимный член определяется «спецификатором структуры без тега» или «спецификатором объединения без тега». Другими словами, union ab;
не указывает анонимного члена; он имеет тег и является повторным объявлением типа union ab
(который имеет неопределенное поведение по другим причинам). Таким образом, анонимный член союза в соответствии с 6.7.2.1 13 должен иметь форму union { member declarations here };
. (Поскольку объявление union ab;
имеет неопределенное поведение по стандарту, его можно использовать для расширения, как указано в ответах.)
Согласно этому ответу, решение для gcc состоит в том, чтобы скомпилировать его с помощью -fms-extensions
.
Вот пример. Протестировано с Visual C++ и MSYS2 gcc 10.2.
В этой версии gcc подразумевается -fms-extensions
, и я получаю сообщение об ошибке, как указано в комментариях, если вместо этого компилирую с -fno-ms-extensions
.
#include <stdio.h>
union ab {
struct {
int a;
} a_st;
struct {
int b;
} b_st;
};
typedef struct d {
union ab;
struct {
int c;
} c_st;
} d_st;
int main(void) {
d_st x;
printf("%zu\n", sizeof(d_st));
x.a_st.a = 1;
x.b_st.b = 2;
x.c_st.c = 3;
printf("a_st = %d\n", x.a_st.a);
printf("b_st = %d\n", x.b_st.b);
printf("c_st = %d\n", x.c_st.c);
return 0;
}
Странно, У меня много ошибок.
спасибо, интересно. я использую gcc 8.4.0. это может быть ошибка в этой версии gcc?
@TomDov Может быть, вам нужно дать возможность -std=c11
gcc. Согласно этому, анонимные структуры и объединения поддерживаются с версии 4.6.
@KamilCuk Действительно, это странно. Он компилируется с gcc -Wall -std=c11
без предупреждения и работает как положено. Это gcc из MSYS2, но я не думаю, что это будет иметь значение. Однако cl /Wall
предупреждает меня даже с /std:c11
.
union ab;
не является анонимным союзом - у него есть тег, ab
и есть тег. Анонимный союз — это союз без тега (название после union
).
@KamilCuk Подождите, тег или идентификатор?
@ Jean-ClaudeArbaut Я использовал c11, но недостающая часть была -fms-extensions
выяснена из вашего комментария к исходному вопросу. поскольку вы были первыми, кто это заметил, можете ли вы включить это в свой ответ, чтобы отметить его как избранный?
Просто путаница. Стандарт C понятен C11 6.7.2.1p13An unnamed member whose type specifier is a structure specifier with no tag is called an anonymous structure
. Он должен быть безымянным и не иметь тега. struct ab;
не имеет имени (идентификатора), у него есть тег - ab
. Документация gcc и документация Microsoft, похоже, используют неназванные/анонимные синонимы.
@TomDov Готово. На самом деле, это также объясняет, почему у меня это сработало: вопреки тому, во что я верил, большая разница, что я тестировал gcc из MSYS2 (то есть в Windows), потому что опция -fms-extensions
всегда включена по умолчанию.
В качестве расширения компилятор MSVC (см. Специальные анонимные структуры Microsoft ) и компилятор gcc с -fms-extensions
(см. безымянные поля документов gcc) поддерживают синтаксис безымянных структур. При использовании этого расширения код компилируется и получает доступ к неназванным членам структуры, как если бы они были членами содержащей структуры.
Если вы используете это расширение, код скомпилируется, и доступ будет в порядке, вы только что сделали опечатку c_st
, должно быть d_st
.
#include <stdio.h>
union ab {
struct {
int a;
} a_st;
struct {
int b;
} b_st;
};
typedef struct d {
union ab;
} d_st;
int main(void) {
d_st c;
printf("%d\n", c.a_st.a);
}
Без использования этого расширения код не будет компилироваться, как описано ниже.
это обязательно зло или я что-то здесь пропустил?
Я бы сказал, что это просто зло. Проблема в следующей части:
struct c {
union ab;
};
Это все равно, что написать:
struct c {
};
Вы можете поставить любой тип и следовать за ним с помощью ;
, например:
struct d {
int; float; unsigned long long; FILE *; some_other_type;
};
union ab
это просто тип. some_type ;
ничего не объявляет, ничего не происходит. Грамматика языка просто позволяет вам написать просто тип как выражение без идентификатора (т.е. он используется в объявлении функции, например void func(char*, int)
), но ничего не происходит, и такое выражение не имеет смысла. Ваш struct c
просто пуст, в нем нет участников, это неопределенное поведение.
пытаясь напрямую «достучаться» до элементов объединения:
Вы не можете получить к ним доступ, потому что они не существуют. Внутри нет элементов struct c
- он пустой.
Вы можете дублировать код (возможно, с помощью макроса):
struct c {
union { // anonymous union
struct {
int a;
} a_st;
struct {
int b;
} b_st;
};
};
и доступ через:
struct c C;
printf("%d\n", C.a_st.a);
union ab;
это не ничто, это безымянный союз. gcc.gnu.org/onlinedocs/gcc/Unnamed-Fields.html#Unnamed-FieldsIf -fms-extensions is used, the field may also be a definition with a tag such as
это важная часть.