Accessing child variables through higher level structures

后端 未结 8 1810
孤街浪徒
孤街浪徒 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: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 
    
    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 
    
    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.

    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...

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

提交回复
热议问题