【问题标题】:Accessing child variables through higher level structures通过更高级别的结构访问子变量
【发布时间】:2017-06-13 13:58:11
【问题描述】:

如果我有这些结构:

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

通常您会通过b.f.x 访问x,但有没有办法设置它以便您可以访问元素x 而无需参考f

bar b;
b.x = ...

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

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

【问题讨论】:

    标签: c++ c


    【解决方案1】:

    你可以用 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 是否可以作为没有标签的 struct 说明符是可以接受的 标准规定:

    § 6.7.8 -- 3

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

    (强调我的)——但是 synonym 是否也意味着 typdef-name 说明符可以替换为 struct 说明符gcc 接受,clang 不接受。

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

    关于您对名称冲突的疑问,这是gcc 在您将另一个int x 放入bar 时必须说的:

    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("%d\n", b.x);
    }
    

    但是任何符合标准的编译器都应该接受这个代码,所以坚持这个版本。

    很遗憾,我更喜欢gcc 接受的语法,但由于标准的措辞并没有明确允许这样做,唯一的安全的赌注是假设它是被禁止的,所以clang在这里不应该受到责备......

    如果您想通过 either 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("%d\n", b.f.x); // <-- guaranteed to print 1
    }
    

    不会因为

    而导致别名问题

    § 6.5.2.3 -- 6

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

    【讨论】:

    • 很棒的反应。这确实回答了这个问题。我一直在寻找匿名结构,而且我很有可能正在研究 C++11 特定代码。
    • @Stewart 请注意,我的整个回复都在谈论 C11,不是 C++11。我不知道这是否也适用于 C++11。
    【解决方案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++: 与在 C 中一样,您可以在此处执行此操作,但匿名结构不是任何 C++ 标准的一部分,而是扩展。 最好使用继承,或者根本不使用此快捷方式。

    当然,不要使用#define x b.x))之类的东西。

    【讨论】:

    • 我想这一定是我过去看到的。我记得很多关于结构对齐的问题。当然#define 是正确的。
    • 如果你在 union 中有完全相同的 struct 定义,就不会有任何对齐问题。
    • @Yuki 因此,#define 这样做实际上是一个好主意
    • 请您详细说明一下,这是个好主意吗?因为我看到了一个问题:您不能在定义宏的任何地方键入x
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

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

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

      1. 是的,这适用于结构。但是按照标准:
        • 经过适当转换的结构指针指向第一个成员。
        • 经过适当转换的联合指针指向任何联合成员。
        • 所以它们在内存中的位置是一样的。

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

          在 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,没有解决成员名称冲突的方法。

          【讨论】:

            【解决方案7】:

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

            在 C 中,这是不可能的。

            【讨论】:

              【解决方案8】:

              由于C标准保证结构的第一个成员之前没有填充,bar中的foo之前没有填充,@中的x之前没有填充987654324@。因此,对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++ 通过继承提供了一种更优雅的方式。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2022-12-17
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多