匿名の大きなメリットに出くわしunion
ました。ただし、これは気弱な人のための話ではなく、推奨される方法でもないことに注意してください。
注:c99にない構造体内の匿名共用体も参照してください。
何百ものソースコードファイルの古いCプログラムには、メンバーとしてstruct
を含むグローバル変数aがあります。struct
したがって、グローバル変数の型定義は次のようになります。
typedef struct {
LONG lAmount;
STRUCTONE largeStruct; // memory area actually used for several different struct objects
ULONG ulFlags;
} STRUCTCOMMON;
、struct
STRUCTONEは、いくつかの大きな構造体の1つでしたが、このコードが記述された時点では、他の構造体はすべてSTRUCTONEよりも小さかったです。したがって、このメモリ領域は、largeStruct
として使用されていましたunion
が、そのことを示す適切なソースステートメントがありませんでした。代わりstruct
に、を使用してさまざまな変数がこの領域にコピーされましたmemcpy()
。さらに悪いことに、これはグローバル変数の実際の名前による場合もあれば、グローバル変数へのポインターによる場合もあります。
時間の経過とともに通常発生するように、最近の変更により、他の構造体の1つが最大になりました。そして、さまざまなエイリアスやその他すべてのファイルとともに、これがどこで使用されているかを探すために、100個のファイルを調べる必要がありました。
そして、匿名の組合を思い出しました。そこで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
この同じソースコード本体で作業しているときに、同じ長さであると想定されていたいくつかの異なる構造体の1つからの日付を含む可能性のあるバイナリレコードを使用して、この手法のアプリケーションに遭遇しました。私が見つけた問題は、プログラマーのエラーが原因でした。1つの構造体が他の構造体とはサイズが異なっていました。
この問題を修正する一環として、コンパイラがデータ構造の正しいサイズを把握できるようにするソリューションが必要でした。
これらの構造体には、すべて同じサイズにするためにパディング変数が追加された構造体のいくつかのメンバーにいくつかの違いが含まれていたため、構造体の1つを除いて正常に機能する匿名の共用体を使用しました。
ユニオンの一部として匿名構造体を追加できることがわかりました。これにより、ユニオンのさまざまなメンバーと追加された匿名構造体の名前が異なる限り、VisualStudio2015で正常にコンパイルされます。
重要な注意:このソリューションでは#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; /* */ \
}; \
};
次に、ファイルから読み取られたデータレコード内のバイナリデータにアクセスするために使用されるいくつかの構造体のうちの3つのサンプルのようにそれを使用しました。
/* 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 );
}
// .......