Accessing child variables through higher level structures

后端 未结 8 1767
孤街浪徒
孤街浪徒 2020-12-09 17:41

If I have these structures:

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

Normally you would access x through

相关标签:
8条回答
  • 2020-12-09 17:52

    C: Highly unrecommended, but doable:

    #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;
    }
    

    Anonymous structs are a widly-spread extension in standards before C11.

    C++: The same as in C, you can do here but anonymous structs are not part of any C++ standard, but an extension. Better use inheritance, or do not use this shortcut at all.

    Of course, do not use something like #define x b.x)).

    0 讨论(0)
  • 2020-12-09 17:55

    Since the C standard guarantees that there isn't padding before the first member of a struct, there isn't padding before the foo in bar, and there isn't padding before the x in foo. So, a raw memory access to the start of bar will access bar::foo::x.

    You could do something like this:

    #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;
    }
    

    As others have noted, C++ offers a more elegant way of doing this through inheritance.

    0 讨论(0)
  • 2020-12-09 17:56

    You can with C11:

    § 6.7.2.1 -- 11

    An unnamed member whose type specifier is a structure specifier with no tag is called an anonymous structure; an unnamed member whose type specifier is a union specifier with no tag is called an anonymous union. The members of an anonymous structure or union are considered to be members of the containing structure or union. This applies recursively if the containing structure or union is also anonymous.

    So this code might work:

    #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);
    }
    

    The problem here is that different compilers disagree in my tests on whether a typedef is acceptable as a struct specifier with no tag The standard specifies:

    § 6.7.8 -- 3

    In a declaration whose storage-class specifier is typedef, each declarator defines an identifier to be a typedef name that denotes the type specified for the identifier in the way described in 6.7.6. [...] A typedef declaration does not introduce a new type, only a synonym for the type so specified.

    (emphasis mine) -- But does synonym also mean a typdef-name specifier is exchangeable for a struct specifier? gcc accepts this, clang doesn't.

    Of course, there's no way to express the whole member of type foo with these declarations, you sacrifice your named member f.

    Concerning your doubt about name collisions, this is what gcc has to say when you put another int x inside bar:

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

    To avoid ambiguity, you can just repeat the struct, possibly #defined as a macro, but of course, this looks a bit ugly:

    #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);
    }
    

    But any conforming compiler should accept this code, so stick to this version.

    <opinion>This is a pity, I like the syntax accepted by gcc much better, but as the wording of the standard doesn't make it explicit to allow this, the only safe bet is to assume it's forbidden, so clang is not to blame here...</opinion>

    If you want to refer to x by either b.x or b.f.x, you can use an additional anonymous union like this:

    #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
    }
    

    This will not cause aliasing issues because of

    § 6.5.2.3 -- 6

    One special guarantee is made in order to simplify the use of unions: if a union contains several structures that share a common initial sequence (see below), and if the union object currently contains one of these structures, it is permitted to inspect the common initial part of any of them anywhere that a declaration of the completed type of the union is visible. Two structures share a common initial sequence if corresponding members have compatible types (and, for bit-fields, the same widths) for a sequence of one or more initial members

    0 讨论(0)
  • 2020-12-09 18:01

    In C++, you can use inheritance and member name conflicts are sort of resolvable with :: and treating the base classes as members.

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

    In standard C, it's impossible, however several compilers (gcc, clang, tinycc) support a similar thing as an extension (usually accessible with -fms-extensions (on gcc also with -fplan9-extensions which is a superset of -fms-extensions)), which allows you to do:

    struct foo { int x; };
    struct bar { struct foo; };
    struct bar b = { 42 }; 
    int main()
    {
       return b.x;
    }
    

    However, there's no resolution for conflicting member names with it, AFAIK.

    0 讨论(0)
  • 2020-12-09 18:06

    This is not possible in C. In C++ however you can use inheritance which is probably what you were thinking about.

    0 讨论(0)
  • 2020-12-09 18:09

    In C you can't access members of members like this.

    You can however access members of an anonymous inner struct:

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

    In C++ you use inheritance:

    struct foo {
        int x;
    };
    
    struct bar: public foo {
    };
    
    ...
    struct bar b;
    b.x = 1;
    
    0 讨论(0)
提交回复
热议问题