Доступ к дочерним переменным через структуры более высокого уровня
Если у меня есть эти структуры:
typedef struct { int x; } foo;
typedef struct { foo f; } bar;
Обычно вы должны обращаться к x
через b.f.x
, но есть ли способ установить это, чтобы вы могли получить доступ к элементу x
без ссылки на f
?
bar b;
b.x = ...
Моя первая интуиция заключается в том, что вы не можете, так как существует вероятность конфликтов имен, если у двух подструктур есть элемент x, и я не могу понять, что такое ошибка компиляции. Однако, я помню, что работал в некоторых рамках, где это было возможно.
В С++ я работал в среде, где bar
существовал, и вы могли получить доступ к своим членам как переменные-члены this->x
из другого класса. Я пытаюсь понять, как это можно сделать.
Ответы
Ответ 1
Вы можете использовать C11:
§ 6.7.2.1 - 11
Неименованный элемент, спецификатор типа которого является спецификатором структуры без тега, называется анонимная структура; неназванный член, спецификатор типа которого является спецификатором объединения с no tag не называется анонимным объединением. Члены анонимной структуры или объединения считаются членами структуры или объединения. Это относится рекурсивно, если содержащая структура или объединение также анонимны.
Итак, этот код может работать:
#include <stdio.h>
typedef struct { int x; } foo;
typedef struct { foo; } bar;
int main(void)
{
bar b;
b.x = 1;
printf("%d\n", b.x);
}
Проблема заключается в том, что разные компиляторы не согласны в моих тестах на то, является ли typedef приемлемым как спецификатор структуры без тега. Стандарт указывает:
§ 6.7.8 - 3
В объявлении, спецификатор класса хранения которого typedef
, каждый декларатор определяет Идентификатор - это имя typedef, которое обозначает тип, указанный для идентификатора, таким образом описанных в 6.7.6. [...] Объявление typedef
не вводит новый тип, только a синоним для указанного типа.
(выделение мое). Но означает ли синоним также, что спецификатор typdef-name заменяется для спецификатора структуры? gcc
принимает это, clang
не делает.
Конечно, нет способа выразить весь член типа foo
этими декларациями, вы жертвуете своим именованным членом f
.
Что касается ваших сомнений в столкновении имен, это то, что gcc
должно сказать, когда вы помещаете еще один int x
внутри bar
:
structinherit.c:4:27: error: duplicate member 'x'
typedef struct { foo; int x; } bar;
^
Чтобы избежать двусмысленности, вы можете просто повторить struct, возможно #define
d в качестве макроса, но, конечно, это выглядит немного уродливо:
#include <stdio.h>
typedef struct { int x; } foo;
typedef struct { struct { int x; }; } bar;
int main(void)
{
bar b;
b.x = 1;
printf("%d\n", b.x);
}
Но любой соответствующий компилятор должен принять этот код, поэтому придерживайтесь этой версии.
<opinion> Очень жаль, мне нравится, что синтаксис, принятый gcc
, намного лучше, но поскольку формулировка стандарта не делает его явным, чтобы разрешить это, единственная безопасная ставка заключается в том, чтобы предположить, что это запрещено, поэтому clang
здесь не виноват... </opinion>
Если вы хотите обратиться к x
с помощью b.x
или b.f.x
, вы можете использовать дополнительный анонимный союз следующим образом:
#include <stdio.h>
typedef struct { int x; } foo;
typedef struct {
union { struct { int x; }; foo f; };
} bar;
int main(void)
{
bar b;
b.f.x = 2;
b.x = 1;
printf("%d\n", b.f.x); // <-- guaranteed to print 1
}
Это не вызовет проблемы с псевдонимом из-за
§ 6.5.2.3 - 6
Для упрощения использования союзов существует одна специальная гарантия: если объединение содержит несколько структур, которые имеют общую начальную последовательность (см. ниже), и если объект объединения в настоящее время содержит одну из этих структур, разрешено проверяйте общую начальную часть любого из них в любом месте, где видна декларация завершенного типа соединения. Две структуры имеют общую начальную последовательность, если соответствующие члены имеют совместимые типы (и для бит-полей, одинаковые ширины) для последовательности одного или нескольких начальных элементов
Ответ 2
C: очень не рекомендуется, но выполнимо:
#include <stdio.h>
#define BAR_STRUCT struct { int x; }
typedef BAR_STRUCT bar;
typedef struct {
union {
bar b;
BAR_STRUCT;
};
} foo;
int main() {
foo f;
f.x = 989898;
printf("%d %d", f.b.x, f.x);
return 0;
}
Анонимные структуры - это расширенное расширение стандартов до C11.
С++:
То же, что и в C, вы можете сделать здесь, но анонимные структуры не являются частью любого стандарта С++, а расширения.
Лучше использовать наследование или вообще не использовать этот ярлык.
Конечно, не используйте что-то вроде #define x b.x
)).
Ответ 3
В C вы не можете получить доступ к таким членам, как это.
Однако вы можете обращаться к членам анонимной внутренней структуры:
struct bar {
struct {
int x;
}
};
...
struct bar b;
b.x = 1;
В С++ вы используете наследование:
struct foo {
int x;
};
struct bar: public foo {
};
...
struct bar b;
b.x = 1;
Ответ 4
В C (99 и далее) вы можете получить доступ к общей начальной подпоследовательности членов объединения, даже если они не были последним членом, записанным в 1.
В C11 вы можете иметь анонимных членов профсоюза. Итак:
typedef struct { int x; } foo;
typedef struct {
union {
foo f;
int x;
};
} bar;
- Да, это относится к структурам. Но согласно стандарту:
- Указатель структуры, соответствующим образом преобразованный, указывает на первый элемент.
- Указатель объединения, соответствующим образом преобразованный, указывает на любой член объединения.
- Таким образом, их расположение в памяти одинаковое.
Ответ 5
Это невозможно в C. В С++, однако, вы можете использовать наследование, что, вероятно, о чем вы думали.
Ответ 6
В С++ вы можете использовать наследование, а конфликты имен членов - это разбор разрешимости с ::
и обработка базовых классов как членов.
struct foo { int x; };
struct bar : foo { };
struct foo1 { int x; };
struct bar1 : foo1 { char const* x; };
bar b;
bar1 b1;
int main()
{
return b.x + b1.foo1::x;
}
В стандарте C невозможно, однако несколько компиляторов (gcc, clang, tinycc) поддерживают аналогичную функцию расширения (обычно доступного с помощью -fms-extensions
(на gcc также с -fplan9-extensions
, который является надмножеством -fms-extensions
)), что позволяет:
struct foo { int x; };
struct bar { struct foo; };
struct bar b = { 42 };
int main()
{
return b.x;
}
Однако нет разрешения на конфликтующие имена участников с ним, AFAIK.
Ответ 7
В С++ это возможно двумя способами. Первое - использовать наследование. Второй для bar
содержит ссылочный элемент с именем x
(int &x
) и конструкторы, которые инициализируют x
для ссылки на f.x
.
В C это невозможно.
Ответ 8
Так как стандарт C гарантирует, что перед первым элементом структуры не будет заполнение, перед foo
в bar
не будет заполнения, и до x
в foo
. Таким образом, доступ к необработанной памяти к началу bar
будет иметь доступ к bar::foo::x
.
Вы можете сделать что-то вроде этого:
#include <stdio.h>
#include <stdlib.h>
typedef struct _foo
{
int x;
} foo;
typedef struct _bar
{
foo f;
} bar;
int main()
{
bar b;
int val = 10;
// Setting the value:
memcpy(&b, &val, sizeof(int));
printf("%d\n", b.f.x);
b.f.x = 100;
// Reading the value:
memcpy(&val, &b, sizeof(int));
printf("%d\n", val);
return 0;
}
Как отмечали другие, С++ предлагает более элегантный способ сделать это через наследование.