我刚刚遇到了匿名union 的巨大好处。但是请注意,这不是胆小的人的故事,也不是推荐的做法。
注意:另见Anonymous union within struct not in c99?
在一个包含数百个源代码文件的旧 C 程序中,有一个全局变量 struct,其中包含一个 struct 作为成员。所以全局变量的类型定义看起来像这样:
typedef struct {
LONG lAmount;
STRUCTONE largeStruct; // memory area actually used for several different struct objects
ULONG ulFlags;
} STRUCTCOMMON;
struct,STRUCTONE,是几个大型结构之一,但在编写此代码时,其他结构都小于 STRUCTONE。所以这个内存区域,largeStruct 被用作union,但没有正确的源语句表明这一点。而是使用memcpy() 将各种struct 变量复制到该区域。更糟糕的是,有时这是通过全局变量的实际名称,有时是通过指向全局变量的指针。
随着时间的推移,最近的变化通常会导致其他结构之一成为最大的。而且我不得不浏览一百个文件,以查找这些文件的使用位置以及所有各种别名和其他所有内容。
然后我想起了匿名工会。所以我将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,编译器可能会在各种结构和联合中引入未知填充。
为了标准化匿名union和匿名struct,我创建了以下define。
#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 );
}
// .......