Достижение именованной структуры в безымянном объединении в C

У меня есть следующая структура союза и структур

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; } Вам не приходит предупреждение?
KamilCuk 10.12.2020 11:12

@Jean-ClaudeArbaut - выбранный ответ из stackoverflow.com/questions/1972003/… сработал, спасибо!

TomDov 10.12.2020 11:42

К вашему сведению, стандарт C 2018 определяет анонимные члены в 6.7.2.1 13. В нем говорится, что анонимный член определяется «спецификатором структуры без тега» или «спецификатором объединения без тега». Другими словами, union ab; не указывает анонимного члена; он имеет тег и является повторным объявлением типа union ab (который имеет неопределенное поведение по другим причинам). Таким образом, анонимный член союза в соответствии с 6.7.2.1 13 должен иметь форму union { member declarations here };. (Поскольку объявление union ab; имеет неопределенное поведение по стандарту, его можно использовать для расширения, как указано в ответах.)

Eric Postpischil 10.12.2020 13:49
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
544
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Согласно этому ответу, решение для 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;
}

Странно, У меня много ошибок.

KamilCuk 10.12.2020 11:28

спасибо, интересно. я использую gcc 8.4.0. это может быть ошибка в этой версии gcc?

TomDov 10.12.2020 11:29

@TomDov Может быть, вам нужно дать возможность -std=c11 gcc. Согласно этому, анонимные структуры и объединения поддерживаются с версии 4.6.

user13963867 10.12.2020 11:34

@KamilCuk Действительно, это странно. Он компилируется с gcc -Wall -std=c11 без предупреждения и работает как положено. Это gcc из MSYS2, но я не думаю, что это будет иметь значение. Однако cl /Wall предупреждает меня даже с /std:c11.

user13963867 10.12.2020 11:36
Msvc, кажется, ест. union ab; не является анонимным союзом - у него есть тег, ab и есть тег. Анонимный союз — это союз без тега (название после union).
KamilCuk 10.12.2020 11:41

@KamilCuk Подождите, тег или идентификатор?

user13963867 10.12.2020 12:05

@ Jean-ClaudeArbaut Я использовал c11, но недостающая часть была -fms-extensions выяснена из вашего комментария к исходному вопросу. поскольку вы были первыми, кто это заметил, можете ли вы включить это в свой ответ, чтобы отметить его как избранный?

TomDov 10.12.2020 12:07

Просто путаница. Стандарт 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, похоже, используют неназванные/анонимные синонимы.

KamilCuk 10.12.2020 12:07

@TomDov Готово. На самом деле, это также объясняет, почему у меня это сработало: вопреки тому, во что я верил, большая разница, что я тестировал gcc из MSYS2 (то есть в Windows), потому что опция -fms-extensions всегда включена по умолчанию.

user13963867 10.12.2020 12:21

В качестве расширения компилятор 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-Field‌​s
TomDov 10.12.2020 11:42
If -fms-extensions is used, the field may also be a definition with a tag such as это важная часть.
KamilCuk 10.12.2020 11:45

Другие вопросы по теме