我刚刚遇到了匿名的巨大好处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 );
}
// .......