11

我发现一个代码实现为如下所示的类似演示..

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

6.58struct/union内的未命名字段structs/unions

允许ISO C11

但是它有什么好处呢?

因为无论如何我都可以以相同的方式访问数据成员

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

在 gcc 4.5.2 上编译,使用 ,

gcc -Wall demo.c -o demo 

并且没有错误,

4

4 回答 4

16

它不必是结构内的匿名结构,我觉得这不是很有用:这通常只会通过引入更多填充来稍微改变布局,没有其他可见效果(与将子结构的成员内联到父结构)。

我认为匿名结构/联合的优势在别处:它们可用于将匿名结构放置在联合中或将匿名联合放置在结构中。

例子:

union u
{
  int i;
  struct { char b1; char b2; char b3; char b4; };
};
于 2012-11-14T10:29:20.387 回答
14

好处是很明显的,不是吗?它使程序员免于想出一个名字!由于命名事物很困难,因此如果没有真正需要,可以避免这样做是很好的。

这也是一个非常明确的信号,表明这struct是本地的,并且从未在其他任何地方使用,但在作为父结构中的字段的上下文中,这是非常非常好的信息,因为它减少了不必要的耦合的可能性

把它想象成static; 它限制了内部struct对外部的可见性,其方式类似于(但当然不等同于)如何static将全局符号的可见性限制在它们出现的编译单元中。

于 2012-11-14T09:55:42.067 回答
4

我刚刚遇到了匿名的巨大好处union。但是请注意,这不是胆小的人的故事,也不是推荐的做法。

注意:另请参见struct not in c99 中的匿名联合?

在包含数百个源代码文件的旧 C 程序中,有一个全局变量 a struct,其中包含 astruct作为成员。所以全局变量的类型定义看起来像这样:

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

STRUCTONEstruct是几个大型结构之一,但在编写此代码时其他结构都小于 STRUCTONE。所以这个内存区域largeStruct被用作一个union但没有正确的源语句表明这一点。相反,各种struct变量被复制到这个区域使用memcpy(). 更糟糕的是,有时这是通过全局变量的实际名称,有时是通过指向全局变量的指针。

随着时间的推移,最近的变化通常会导致其他结构之一成为最大的。而且我不得不浏览一百个文件,以寻找这些文件以及所有各种别名和其他所有内容的使用位置。

然后我想起了匿名工会。因此,我将其修改typedef为以下内容:

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;

然后重新编译所有东西。

所以现在那些我不高兴地期待的源代码审查和回归测试不再需要了。

我现在可以开始使用这个全局来慢慢修改源代码,以便在我自己的时间表上将此源代码提升到更现代的标准。

附录 - 匿名struct中的匿名union

在同一个源代码体中工作时,我遇到了这种技术的应用程序,它带有一个二进制记录,其中可能包含来自几个不同结构之一的日期,这些结构应该是相同的长度。我发现的问题是由于程序员错误,一个结构的大小与其他结构不同。

作为纠正这个问题的一部分,我想要一个允许编译器计算出数据结构的正确大小的解决方案。

由于这些结构在结构的几个成员中包含一些差异,并添加了填充变量以使它们都具有相同的大小,因此我选择了匿名联合,除了其中一个结构外,它工作得很好。

我发现我可以添加一个匿名结构作为联合的一部分,这样只要联合的各个成员和添加的匿名结构具有不同的名称,它就可以在 Visual Studio 2015 中正常编译。

重要提示:此解决方案需要#pragma pack(1)使用 Visual Studio 2015 在字节边界上打包结构和联合。如果不使用pragma编译器,可能会在各种结构和联合中引入未知填充。

我创建了以下内容define以标准化匿名union和匿名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;                           /* */  \
        };  \
    };

然后将其用作此示例中的三个结构,这些结构用于访问从文件读取的数据记录中的二进制数据。

    /* 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;

原本是

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;

使用union所有各种记录类型,如下所示:

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;

这些记录格式用在类似于下面的代码中:

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

    //    .......
于 2016-02-18T23:49:24.170 回答
0

我在开发将通过指针访问的连续地址结构时使用了匿名结构。更具体地说,我将在父结构中使用匿名结构来启用对内存的某些部分进行位字段处理,这些部分被划分为标记数据的较小部分。

注意编译器如何打包位域信息,位域结构的第一个成员可以是 LSB 或 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;
于 2021-11-04T13:07:02.593 回答