通过更高级别的结构访问子变量
如果我有这些结构:
typedef struct { int x; } foo; typedef struct { foo f; } bar;
通常你会通过bfx
访问x
,但有没有办法设置它,这样你就可以访问元素x
而不参考f
?
bar b; bx = ...
我的第一个直觉是你不能,因为如果两个子结构都有一个成员x而我无法弄清楚编译错误会是什么,那么就有可能发生名称冲突。 但是,我记得在一些可行的框架中工作。
在C ++中,我曾在一个框架中工作过bar
,并且你可以从其他类访问其成员作为成员变量this->x
。 我想弄清楚如何做到这一点。
你可以用C11:
§6.7.2.1 – 11
类型说明符是没有标记的结构说明符的未命名成员称为匿名结构; 一个未命名的成员,其类型说明符是一个没有标记的联合说明符,称为匿名联合。 匿名结构或联合的成员被视为包含结构或联合的成员。 如果包含的结构或联合也是匿名的,则递归应用。
所以这段代码可能有效:
#include typedef struct { int x; } foo; typedef struct { foo; } bar; int main(void) { bar b; bx = 1; printf("%d\n", bx); }
这里的问题是不同的编译器在我的测试中不同意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 typedef struct { int x; } foo; typedef struct { struct { int x; }; } bar; int main(void) { bar b; bx = 1; printf("%d\n", bx); }
但任何符合标准的编译器都应接受此代码 ,因此请坚持使用此版本。
<意见>这很可惜,我更喜欢gcc
接受的语法,但由于标准的措辞没有明确允许这样做,唯一安全的选择是假设它被禁止,所以clang
不是责怪这里…… opinion>
如果要通过bx
或 bfx
引用x
,可以使用另外的匿名联合,如下所示:
#include typedef struct { int x; } foo; typedef struct { union { struct { int x; }; foo f; }; } bar; int main(void) { bar b; bfx = 2; bx = 1; printf("%d\n", bfx); // <-- guaranteed to print 1 }
这不会导致别名问题,因为
§6.5.2.3 - 6
为了简化联合的使用,我们做了一个特殊的保证:如果一个联合包含几个共享一个共同初始序列的结构(见下文),并且如果联合对象当前包含这些结构中的一个,则允许检查公共其中任何一个的初始部分都可以看到完整类型的联合声明。 如果对应的成员具有一个或多个初始成员的序列的兼容类型(并且对于位字段,具有相同的宽度),则两个结构共享共同的初始序列
C :非常不推荐,但可行:
#include #define BAR_STRUCT struct { int x; } typedef BAR_STRUCT bar; typedef struct { union { bar b; BAR_STRUCT; }; } foo; int main() { foo f; fx = 989898; printf("%d %d", fbx, fx); return 0; }
在C11之前,匿名结构是标准的广泛扩展。
C ++ :与C中相同,你可以在这里做,但匿名结构不是任何C ++标准的一部分,而是扩展。 更好地使用inheritance,或者根本不使用此快捷方式。
当然,不要使用#define x bx
))。
在C中,您无法访问此类成员。
但是,您可以访问匿名内部结构的成员:
struct bar { struct { int x; } }; ... struct bar b; bx = 1;
在C ++中,您使用inheritance:
struct foo { int x; }; struct bar: public foo { }; ... struct bar b; bx = 1;
在C(99及以后)中,您可以访问联合成员的公共初始子序列,即使它们不是写入1的最后一个成员。
在C11中,您可以拥有匿名工会成员。 所以:
typedef struct { int x; } foo; typedef struct { union { foo f; int x; }; } bar;
- 是的,这适用于结构。 但根据标准:
- 适当转换的结构指针指向第一个成员。
- 适当转换的联合指针指向任何联合成员。
- 所以他们在记忆中的位置是一样的。
这在C中是不可能的。但是在C ++中你可以使用inheritance,这可能就是你想到的。
在C ++中,您可以使用inheritance,并且成员名称冲突可以通过::
解析,并将基类视为成员。
struct foo { int x; }; struct bar : foo { }; struct foo1 { int x; }; struct bar1 : foo1 { char const* x; }; bar b; bar1 b1; int main() { return bx + b1.foo1::x; }
在标准C中,这是不可能的,但是一些编译器(gcc,clang,tinycc)支持与扩展类似的东西(通常可以使用-fms-extensions
访问(在gcc上也使用-fplan9-extensions
,它是-fplan9-extensions
的超集) )),它允许你这样做:
struct foo { int x; }; struct bar { struct foo; }; struct bar b = { 42 }; int main() { return bx; }
但是,AFAIK没有解决与之冲突的成员名称的问题。
在C ++中,它有两种可能。 首先是使用inheritance。 第二个是bar
包含名为x
( int &x
)的引用成员,以及初始化x
以引用fx
构造函数。
在C中,这是不可能的。
由于C标准保证在结构的第一个成员之前没有填充,因此在foo
中的bar
之前没有填充,并且在foo
的x
之前没有填充。 因此,对bar
开头的原始内存访问将访问bar::foo::x
。
你可以这样做:
#include #include 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", bfx); bfx = 100; // Reading the value: memcpy(&val, &b, sizeof(int)); printf("%d\n", val); return 0; }
正如其他人所说,C ++通过inheritance提供了一种更优雅的方式。