如果我有这些结构:
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;
^
为了避免歧义,你可以只重复结构,可能#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("%dn", b.x);
}
但是任何符合标准的编译器都应该接受此代码,因此请坚持使用此版本。
<意见>很遗憾,我更喜欢gcc
接受的语法,但由于标准的措辞没有明确允许这样做,唯一安全的选择是假设它是被禁止的,所以这里不应该责怪clang
......意见>
如果要通过b.x
或b.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;
- 是的,这适用于结构。但根据标准:
- 经过适当转换的结构指针指向第一个成员。
- 经过适当转换的工会指针指向任何工会成员。
- 所以它们在内存中的位置是相同的。
这在 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 标准保证在结构的第一个成员之前没有填充,因此在bar
中foo
之前没有填充,并且在foo
x
之前没有填充。 因此,对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++提供了一种通过继承来做到这一点的更优雅的方式。