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

时间:2017-06-13 13:58:12

标签: c++ c

如果我有这些结构:

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

通常您可以通过x访问b.f.x,但有没有办法进行设置,以便您可以访问元素x而无需引用f

bar b;
b.x = ...

我的第一个直觉是你不能,因为如果两个子结构都有一个成员x而我无法弄清楚编译错误会是什么,那么就有可能发生名称冲突。但是,我记得在一些可行的框架中工作。

在C ++中,我曾在bar存在的框架中工作,您可以从其他类访问其成员作为成员变量this->x。我想弄清楚如何做到这一点。

8 个答案:

答案 0 :(得分:18)

你可以使用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("%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;
                           ^

为了避免含糊不清,你可以重复结构,可能#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);
}

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

&lt; views&gt;这很遗憾,我更喜欢gcc接受的语法,但是由于标准的措辞不能使显式允许这样做,唯一的安全赌注是假设它是被禁止的,所以clang不应该责备在这里......&lt; / opinion&gt;

如果您想通过 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

  

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

答案 1 :(得分:7)

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)的内容。

答案 2 :(得分:5)

在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;

答案 3 :(得分:3)

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

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

typedef struct { int x; } foo;
typedef struct {
  union {
    foo f;
    int x;
  };
} bar;
  1. 是的,这适用于结构。但根据标准:
    • 适当转换的结构指针指向第一个成员。
    • 适当转换的联合指针指向任何联盟成员。
    • 所以他们在记忆中的位置是一样的。

答案 4 :(得分:2)

这在C语言中是不可能的。但是在C ++中你可以使用继承,这可能就是你想到的。

答案 5 :(得分:2)

在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。

答案 6 :(得分:0)

在C ++中,它有两种可能。首先是使用继承。第二个是import {Header} from './Header.jsx'; 包含名为barx)的引用成员,以及初始化int &x以引用x的构造函数。

在C中,这是不可能的。

答案 7 :(得分:0)

由于C标准保证在结构的第一个成员之前没有填充,因此在foo中的bar之前没有填充,并且在x之前没有填充在foo中{1}}。因此,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;
}

正如其他人所说,C ++通过继承提供了一种更优雅的方式。