次の2つの例を検討してください。
1。
_union test{
struct {
int a;
int b[];
};
};
int main(void){
union test test;
test.a = 10;
printf("test.b[0] = %d", test.b[0]); //prints 0, UB?
}
_
2。
_#include <stdio.h>
union test{
int a;
int b[]; //error: flexible array member in union
};
int main(void){
union test test;
test.a = 10;
printf("test.b[0] = %d", test.b[0]);
}
_
動作は不明です。 6.7.2.1(p13)
なので、これらの例は同じように動作します(つまり、最初の例もコンパイルに失敗します)。
匿名構造または共用体のメンバーは、包含構造または共用体のメンバーと見なされます。
そのため、union
に匿名のstruct
がメンバーとして含まれるかのように表現を解釈しました。匿名のstruct
のメンバーは、それを含むunion
のメンバーと見なされます。
Question:最初の例が2番目の例として失敗するのではなく、うまくコンパイルされるのはなぜですか?
匿名構造または共用体のメンバーは、包含構造または共用体のメンバーと見なされます。
これは解釈が難しい規定であり、実際、これは標準に対する少なくとも2つの欠陥報告の対象となっています。 DR 499 に対する委員会の対応で意図されているのは、レイアウト自体が構造自体が包含構造または共用体のメンバーであるかのように、匿名構造がレイアウト目的で扱われることですaccessのメンバーへのアクセスは、それらが包含構造またはユニオンのメンバーであるかのように表現されます。
一方、 DR 502 で受け入れられた位置は、フレキシブル配列メンバーをonlyメンバーとして含む匿名の構造体であっても、それがそれを含む構造体(ユニオンではない)、および少なくとも1つの構造体がその前にあります。
私はそれらに少し一貫性がないと思いますが、それら全体の統一テーマは、この領域の標準の意図がlayoutに帰着することであるようです。匿名構造体内の柔軟な配列メンバーは、最も内側の名前付き構造体または共用体のレイアウトの最後にある限り許可されます。これは、メンバーの事実を考慮して、他のメンバーの考慮からゼロ以外のサイズでなければなりません匿名構造体が共用体の内部にあるかどうかに関係なく、匿名構造体の重複はありません。
(C11)標準は §6.7.2.1構造体と共用体指定子¶ で述べています—制約:
¶3構造体または共用体は、不完全または関数型のメンバーを含んではなりません(したがって、構造体はそれ自体のインスタンスを含んでいませんが、それ自体のインスタンスへのポインターを含むことができます)。ただし、複数の名前付きメンバーが不完全な配列型を持っている可能性があります。そのような構造体(およびそのような構造体であるメンバーを含む可能性のある再帰的なユニオン)は、構造体のメンバーまたは配列の要素であってはなりません。
構造体のみが(直接)柔軟な配列メンバーを含むことができることに注意してください—共用体はできません。
最初のケースは正当です。 2番目は違います。
(それは §6.7.2.1¶18 という用語を定義しています柔軟な配列メンバー)
ちなみに、質問の最初の version では、最初の例のprintf()
ステートメントが、割り当てられていない配列の要素にアクセスしていました。リビジョン2. union test test;
を書き込むと、サイズ0の配列が得られます。動的メモリ割り当て(または他の何らかの メカニズム)を使用する必要があります )空でないFAMに十分なスペースのある共用体または構造体を割り当てるには。同様のコメントが2番目の例にも適用されました。
ただし、最初のケースでは構造が匿名であるため、構造のメンバーは包含ユニオンのメンバーと見なされ、ユニオンに柔軟な配列メンバーを含めるようにする必要があります。引用したとおり匿名の構造体または共用体のメンバーは、包含構造体または共用体のメンバーと見なされます。
匿名構造は、ユニオンに埋め込まれているからといって、形が失われないことに注意してください。 1つの違いは、union test
のb
のオフセットを0にすることはできないことです。これは、共用体の通常のメンバーとは完全に異なります。通常、共用体のすべてのメンバーはオフセット0から始まります。ただし、ほとんどの場合、変数union test u;
を指定すると、u.a
およびu.b
を参照できます。以前は、構造体の名前union test { struct { int a; int b[]; } s; };
を指定し、u.s.a
またはu.s.b
を使用してユニオン内の構造体の要素にアクセスする必要がありました。これは、柔軟な配列メンバーが許可される場所には影響しません。アクセスに使用される表記のみ。
2番目のケースは、フレキシブル配列メンバーが構造体型のプロパティであり、共用体ではないため、コンパイルに失敗します。それは簡単です。
次に、最初のケースでは、b[0]
にアクセスしようとしても、メモリが割り当てられていないため、未定義の動作になります。
引用C11
、§6.7.2.1/ P18
特殊なケースとして、複数の名前付きメンバーを持つ構造体の最後の要素は、不完全な配列型になる場合があります。これは、フレキシブルアレイメンバーと呼ばれます。 [...]この配列に要素がない場合は、1つの要素があるかのように動作しますが、その要素にアクセスしたり、要素の1つ先にポインタを生成しようとしたりした場合の動作は未定義です。
それは言った、
匿名構造または共用体のメンバーは、包含構造または共用体のメンバーと見なされます。
これはアクセスを目的としたもので、レイアウトは変更されていません。最初の例では、ユニオンの直接のメンバーであるかのようにa
(およびb
)にアクセスしています。
明確にするために、
#include <stdio.h>
union test{
struct {
int p;
float q;
} t; //named structure member
struct {
int a;
int b[];
};
char pqr;
};
int main(void){
union test test;
test.t.p = 20; // you have to use the structure member name to access the elements
test.pqr = 'c'; // direct access, as member of union
test.a = 10; // member of anonymous structure, so it behaves as if direct member of union
}
匿名コンポジット内のタグなしのコンポジットがその形状を保持することは、常に意図されていました。しかし、それは6.7.2.1p13の表現では明確ではありませんでした。表現はC18で次のように改訂されました(強調が追加されました):
- タイプ指定子がタグのない構造指定子である名前のないメンバーは、無名構造と呼ばれます。タイプ指定子がタグのない共用体指定子である名前のないメンバーは、無名共用体と呼ばれます。匿名の構造体または共用体のメンバーは、それを含む構造体または共用体のメンバーと見なされ、それらの構造体または共用体のレイアウトを維持します。包含構造または共用体も匿名の場合、これは再帰的に適用されます。
C18標準へのリンクについては http://www.iso-9899.info/wiki/The_Standard を参照してください 無料で利用可能なドラフト(pdf)