通过更高级别的结构访问子变量

Accessing child variables through higher level structures

本文关键字:访问 变量 结构 高级别      更新时间:2023-10-16

如果我有这些结构:

typedef struct { int x; } foo;
typedef struct { foo f; } bar;

通常你会通过b.f.x访问x,但是有没有办法设置它,以便你可以在不参考f的情况下访问元素x

bar b;
b.x = ...

我的第一个直觉是你不能,因为如果两个子结构都有一个成员 x,并且我无法弄清楚编译错误是什么,则可能会出现名称冲突。 但是,我记得在一些可能的情况下工作。

C++,我曾经在一个存在bar的框架中工作过,你可以从不同的类this->x成员变量访问它的成员。 我正试图弄清楚如何做到这一点。

您可以使用C11:

§ 6.7.2.1 -- 11

类型说明符

是没有标记的结构说明符的未命名成员称为 匿名结构;一个未命名的成员,其类型说明符是联合说明符 没有标签称为匿名联合。匿名结构或工会的成员 被视为包含结构或工会的成员。这适用 递归地,如果包含结构或联合也是匿名的。

所以这段代码可能有效:

#include <stdio.h>
typedef struct { int x; } foo;
typedef struct { foo; } bar;
int main(void)
{
bar b;
b.x = 1;
printf("%dn", b.x);
}

这里的问题是,在我的测试中,不同的编译器不同意typedef是否可以作为没有标记的结构说明符该标准指定:

§ 6.7.8 -- 3

在存储类说明符为typedef的声明中,每个声明符定义一个 标识符 是 typedef 名称,表示以 在 6.7.6 中描述。[...]typedef声明不会引入新类型,只会引入 如此指定的类型的同义词。

(强调我的) -- 但是同义词是否也意味着typdef-name说明符可以换成结构说明符gcc接受这一点,clang不接受。

当然,没有办法用这些声明来表达类型foo的整个成员,你牺牲了你的命名成员f

关于您对名称冲突的怀疑,当您将另一个int x放入bar时,gcc不得不说:

structinherit.c:4:27: error: duplicate member 'x'
typedef struct { foo; int x; } bar;
^

为了避免歧义,你可以只重复结构,可能#defined作为宏,但当然,这看起来有点丑陋:

#include <stdio.h>
typedef struct { int x; } foo;
typedef struct { struct { int x; }; } bar;
int main(void)
{
bar b;
b.x = 1;
printf("%dn", b.x);
}

但是任何符合标准的编译器都应该接受此代码,因此请坚持使用此版本。

<意见>很遗憾,我更喜欢gcc接受的语法,但由于标准的措辞没有明确允许这样做,唯一安全的选择是假设它是被禁止的,所以这里不应该责怪clang......

如果要通过b.xb.f.x来引用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("%dn", b.f.x); // <-- guaranteed to print 1
}

会导致混叠问题,因为

§ 6.5.2.3 -- 6

为了简化联合的使用,有一个特殊的保证:如果联合包含多个共享公共初始序列的结构(见下文),并且如果联合对象当前包含其中一个结构,则允许检查其中任何一个的共同初始部分,只要可以看到联合的已完成类型的声明。如果对应成员具有一个或多个初始成员序列的兼容类型(并且对于位字段,具有相同的宽度),则两个结构共享一个公共初始序列

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++: 与 C 相同,您可以在此处执行此操作,但匿名结构不是任何C++标准的一部分,而是扩展。 最好使用继承,或者根本不使用此快捷方式。

当然,不要使用类似#define x b.x))。

在 C 中,你不能像这样访问成员的成员。

但是,您可以访问匿名内部结构的成员:

struct bar {
struct {
int x;
}
};
...
struct bar b;
b.x = 1;

在C++中使用继承:

struct foo {
int x;
};
struct bar: public foo {
};
...
struct bar b;
b.x = 1;

在 C(99 及更高版本)中,您可以访问联合成员的公共初始子序列,即使他们不是写入1的最后一个成员。

在 C11 中,您可以拥有匿名工会成员。所以:

typedef struct { int x; } foo;
typedef struct {
union {
foo f;
int x;
};
} bar;

  1. 是的,这适用于结构。但根据标准:
    • 经过适当转换的结构指针指向第一个成员。
    • 经过适当转换的工会指针指向任何工会成员。
    • 所以它们在内存中的位置是相同的。

这在 C 语言中是不可能的。但是,C++您可以使用继承,这可能是您正在考虑的。

在C++中,您可以使用继承,并且成员名称冲突可以通过::和将基类视为成员来解决。

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。

在C++中,可以通过两种方式实现。 首先是使用继承。 第二种是让bar包含一个名为x(int &x) 的引用成员,以及初始化x以引用f.x的构造函数。

在 C 中,这是不可能的。

由于 C 标准保证在结构的第一个成员之前没有填充,因此在barfoo之前没有填充,并且在foox之前没有填充。 因此,对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("%dn", b.f.x);
b.f.x = 100;

// Reading the value:
memcpy(&val, &b, sizeof(int));
printf("%dn", val);
return 0;
}

正如其他人所指出的,C++提供了一种通过继承来做到这一点的更优雅的方式。