What are the benefits of unnamed structs / unions in C?
Asked Answered
O

4

13

I found one code implemented as the similar demo shown below ..

struct st
{
 int a;
 struct
 {
 int b;
 };
};

6.58 Unnamed struct/union fields within structs/unions

As permitted by ISO C11.

But What are benefits of it ?

Because anyway I can access the data members in a same manner like

int main()
{
 struct st s;
 s.a=11;
 s.b=22;
 return 0;
}

compiled on gcc 4.5.2 with ,

gcc -Wall demo.c -o demo 

and no errors ,

Orgel answered 14/11, 2012 at 9:54 Comment(1)
possible duplicate of What are anonymous structs and unions useful for in C11?Would
S
18

It does not have to be an anonymous struct inside a struct, which I do not find very useful: this will typically only change the layout slightly by introducing more padding, with no other visible effects (compared to inlining the members of the child struct into the parent struct).

I think that the advantage of anonymous struct/unions is elsewhere: they can be used to place an anonymous struct inside an union or an anonymous union inside a struct.

Example:

union u
{
  int i;
  struct { char b1; char b2; char b3; char b4; };
};
Subito answered 14/11, 2012 at 10:29 Comment(3)
Can you explain how to use this union? For example, if I have instance x of u and use x.b1='a', would the rest b2, b3, b4 be initialized and take memory space?Heterophyllous
@Heterophyllous Same as for a traditional (named) struct inside a union. Your question is really about unions containing structs. You should ask it as a SO question instead of as a comment on an answer to a more specific question, but since you did the latter, x.b1='a' does not initialize the members b2, b3, b4 but these do “take memory space”, as can be seen by printing the value of sizeof (union u). In theory, if you declare a union u variable of which you only ever use the b1 member, a sufficiently smart compiler may only reserve memory for b1, …Subito
@Heterophyllous but in principle declaring a union u object means that you may want to write to any of the members of the structs it contains later, so memory should be reserved for them.Subito
L
14

The benefit is pretty obvious, isn't it? It saves the programmer from coming up with a name! Since naming things is hard, it's nice that it's possible to avoid doing so if there is no real need.

It's also a pretty clear signal that this struct is local and never used anywhere else but in the context of being a field in the parent struct, which is really, really nice information since it reduces the possibility of needless coupling.

Think of it as static; it restricts the visibility of the inner struct to the outer one, in a manner similar to (but not, of course, equivalent with) how static restricts the visibility of global symbols to the compilation unit in which they appear.

Lilialiliaceous answered 14/11, 2012 at 9:55 Comment(3)
Little elaborate .. static ?? and one more if that one is local inside then using same name of identifier gives error .. but in function we can give same name identifier inside {} because of block scope why isn't it allowed here struct {}Orgel
The struct becomes so local that it is difficult to understand why the programmer is not inlining its members directly in the parent struct. This answer in its current version does not list any benefit with respect to that alternative. The layout is different between the nested struct as compared to the inlined members (with more padding, which may be the intended effect but would usually be considered a disadvantage).Subito
It seems that an anonymous union is much more advantageous than an anonymous structure, for the reason given above.Culbert
R
4

I just ran into a huge benefit of anonymous union. However be warned this is not a story for the faint hearted nor is it a recommended practice.

Note: See also Anonymous union within struct not in c99?

In an older C program of hundreds of source code files there is a global variable, a struct, which contained a struct as a member. So the type definition for the global variable looked some thing like:

typedef struct {
    LONG       lAmount;
    STRUCTONE  largeStruct;  // memory area actually used for several different struct objects
    ULONG      ulFlags;
} STRUCTCOMMON;

The struct, STRUCTONE, was one of several large structs however the others were all smaller than STRUCTONE at the time this code was written. So this memory area, largeStruct was being used as a union but without the proper source statements indicating so. Instead various struct variables were copied into this area using memcpy(). To make matters worse sometimes this was through the actual name of the global variable and sometimes through a pointer to the global variable.

As typically happens as time progresses recent changes resulted in one of the other structs becoming the largest. And I was faced with having to go through a hundred files looking for where this was being used along with all the various aliases and everything else.

And then I remembered anonymous unions. So I modified the typedef to be the following:

typedef struct {
    LONG       lAmount;
    union {
        // anonymous union to allow for allocation of largest space needed
        STRUCTONE  largeStruct;  // memory area actually used for several different struct objects
        STRUCTTHREE  largerStruct;  // memory area for even larger struct
     };
    ULONG      ulFlags;
} STRUCTCOMMON;

And then recompiled every thing.

So now all those days of source code review and regression testing I was unhappily looking forward to are no longer necessary.

And I can now begin the process of slowly modifying source using this global to bring this source up to more modern standards on my own time table.

Addendum - Anonymous struct within anonymous union

Working in this same source code body I ran into an application of this technique with a binary record that could contain date from one of several different structs which were supposed to be the same length. The problem I found was due to a programmer error, one struct was a different size than the others.

As part of correcting this problem, I wanted a solution that would allow the compiler to figure out the correct sizes for the data structures.

Since these structs contained some differences in a couple of members of the structs with padding variables added to make them all the same size, I went with anonymous unions which worked fine except for one of the structs.

I found I could add an anonymous struct as part of the union so that as long as the various members of the union and the added anonymous struct had different names, it would compile fine with Visual Studio 2015.

Important Note: This solution requires #pragma pack(1) with Visual Studio 2015 to pack the structs and unions on byte boundaries. Without the use of the pragma the compiler may introduce unknown padding into the various structs and unions.

I created the following define in order to standardize the anonymous union and anonymous struct.

#define PROGRPT_UNION_STRUCT  \
    union {  \
        SHORT   sOperand1;                              /* operand 1 (SHORT) */  \
        LONG    lOperand1;                              /* operand 1 (LONG) */  \
        PROGRPT_ITEM Operand1;                          /* operand 1 */  \
        struct {  \
            UCHAR   uchReserved3;                           /* */  \
            USHORT  usLoopEnd;                              /* offset for loop end */  \
            UCHAR   uchReserved4;                           /* */  \
        };  \
    };

Then used it as in this sample of three of the several structs that are used to access the binary data in the data record read from a file.

    /* loop record */
typedef struct {
    UCHAR   uchOperation;                           /* operation code (LOOP) */
    UCHAR   uchRow;                                 /* position (row) */
    UCHAR   uchLoopBrace;                           /* loop brace (begin/end) */
    UCHAR   uchReserved1;                           /* */
    TCHAR   auchReserved2[ 2 ];                     /* */
    UCHAR   uchCondition;                           /* condition code */
    PROGRPT_ITEM LoopItem;                          /* loop record */
    PROGRPT_UNION_STRUCT
    PROGRPT_ITEM Reserved5;                         /* */
} PROGRPT_LOOPREC;

    /* print record */
typedef struct {
    UCHAR   uchOperation;                           /* operation code (PRINT) */
    UCHAR   uchRow;                                 /* position (row) */
    UCHAR   uchColumn;                              /* position (column) */
    UCHAR   uchMaxColumn;                           /* max no of column */
    TCHAR   auchFormat[ 2 ];                        /* print format/style */
    UCHAR   uchCondition;                           /* condition code */
    PROGRPT_ITEM PrintItem;                         /* print item */
    PROGRPT_UNION_STRUCT
    PROGRPT_ITEM Operand2;                          /* ope2 for condition */
} PROGRPT_PRINTREC;

    /* mathematics record ( accumulator.total = LONG (+,-,*,/) opr2) */
typedef struct {
    UCHAR   uchOperation;                           /* operation code (MATH) */
    UCHAR   uchRow;                                 /* position (row) */
    UCHAR   uchColumn;                              /* position (column) */
    UCHAR   uchMaxColumn;                           /* max no of column */
    TCHAR   auchFormat[ 2 ];                        /* format style */
    UCHAR   uchCondition;                           /* condition code */
    PROGRPT_ITEM Accumulator;                       /* accumulator */
    PROGRPT_UNION_STRUCT
    PROGRPT_ITEM Operand2;                          /* operand 2 */
} PROGRPT_MATHTTL;

which were originally

typedef struct {
    UCHAR   uchOperation;                           /* operation code (LOOP) */
    UCHAR   uchRow;                                 /* position (row) */
    UCHAR   uchLoopBrace;                           /* loop brace (begin/end) */
    UCHAR   uchReserved1;                           /* */
    TCHAR   auchReserved2[ 2 ];                     /* */
    UCHAR   uchCondition;                           /* condition code */
    PROGRPT_ITEM LoopItem;                          /* loop record */
    UCHAR   uchReserved3;                           /* */
    USHORT  usLoopEnd;                              /* offset for loop end */
    UCHAR   uchReserved4;                           /* */
    PROGRPT_ITEM Reserved5;                         /* */
} PROGRPT_LOOPREC;

    /* print record */
typedef struct {
    UCHAR   uchOperation;                           /* operation code (PRINT) */
    UCHAR   uchRow;                                 /* position (row) */
    UCHAR   uchColumn;                              /* position (column) */
    UCHAR   uchMaxColumn;                           /* max no of column */
    TCHAR   auchFormat[ 2 ];                        /* print format/style */
    UCHAR   uchCondition;                           /* condition code */
    PROGRPT_ITEM PrintItem;                         /* print item */
    PROGRPT_ITEM Operand1;                          /* ope1 for condition */
    PROGRPT_ITEM Operand2;                          /* ope2 for condition */
} PROGRPT_PRINTREC;

    /* mathematics record ( accumulator.total = LONG (+,-,*,/) opr2) */
typedef struct {
    UCHAR   uchOperation;                           /* operation code (MATH) */
    UCHAR   uchRow;                                 /* position (row) */
    UCHAR   uchColumn;                              /* position (column) */
    UCHAR   uchMaxColumn;                           /* max no of column */
    TCHAR   auchFormat[ 2 ];                        /* format style */
    UCHAR   uchCondition;                           /* condition code */
    PROGRPT_ITEM Accumulator;                       /* accumulator */
    LONG    lOperand1;                              /* operand 1 (LONG) */
    PROGRPT_ITEM Operand2;                          /* operand 2 */
} PROGRPT_MATHTTL;

Using a union of all the various record types that looks like:

typedef union {
    PROGRPT_LOOPREC  Loop;                          /* loop record */
    PROGRPT_PRINTREC Print;                         /* print record */
    PROGRPT_MATHOPE  MathOpe;                       /* math (with operand) */
    PROGRPT_MATHTTL  MathTtl;                       /* math (with total) */
    PROGRPT_MATHCO   MathCo;                        /* math (with count) */
} PROGRPT_RECORD;

These record formats are used in the code similar to the following:

for ( usLoopIndex = 0; usLoopIndex < usMaxNoOfRec; ) {
    ULONG            ulActualRead = 0;       /* actual length of read record function */
    PROGRPT_RECORD   auchRecord;

    /* --- retrieve a formatted record --- */
    ProgRpt_ReadFile( ulReadOffset, &auchRecord, PROGRPT_MAX_REC_LEN, &ulActualRead );
    if ( ulActualRead != PROGRPT_MAX_REC_LEN ) {
        return ( LDT_ERR_ADR );
    }

    /* --- analyze operation code of format record, and
        store it to current row item buffer --- */
    switch ( auchRecord.Loop.uchOperation ) {
    case PROGRPT_OP_PRINT:  /* print operation */
        sRetCode = ProgRpt_FinPRINT( &ReportInfo, &auchRecord.Print, uchMinorClass, NULL );
        break;

    case PROGRPT_OP_MATH:   /* mathematics operation */
        sRetCode = ProgRpt_FinMATH(&auchRecord.MathOpe, NULL );
        break;

    case PROGRPT_OP_LOOP:   /* loop (begin) operation */
        ProgRpt_PrintLoopBegin( &ReportInfo, &auchRecord.Loop );

        switch ( auchRecord.Loop.LoopItem.uchMajor ) {
        case PROGRPT_INDKEY_TERMNO:
            sRetCode = ProgRpt_IndLOOP( &ReportInfo, &auchRecord.Loop, uchMinorClass, usTerminalNo, ulReadOffset );
            usLoopIndex  += auchRecord.Loop.usLoopEnd;
            ulReadOffset += ( PROGRPT_MAX_REC_LEN * auchRecord.Loop.usLoopEnd );
            break;

        default:
            return ( LDT_ERR_ADR );
        }
        break;

    default:
        return ( LDT_ERR_ADR );
    }

    //    .......
Rianna answered 18/2, 2016 at 23:49 Comment(0)
M
0

I've used anonymous structs in developing contiguous address structures that I'll be accessing via pointers. More specifically, I'll use anonymous structs within the parent struct to enable bit-fielding certain portions of the memory that is divided into smaller portions of labeled data.

Be careful of how your compiler packs the bit-fielded information, the first member of the bitfielded struct can either be the LSB or MSB.

typedef struct
{
uint32_t a;
    
    struct 
    {
        uint32_t b : 16;
        uint32_t c : 8;
        uint32_t d : 7;
        uint32_t e : 1;
    };
}Parent;

#define ADDRESS ((Parent*)(uint16_t)0xF0F0) 

ADDRESS->a = data_32_bits;
ADDRESS->b = data_16_bits;
ADDRESS->c = data_8_bits;
ADDRESS->d = data_7_bits;
ADDRESS->e = data_1_bit;
Menjivar answered 4/11, 2021 at 13:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.