C-std セクション 6.7.2.1 から引用すると、
struct s { int n; double d[]; };
これは有効な構造宣言です。この種の構文の実用的な使用法を探しています。正確に言えば、この構造は double* を 2 番目の要素として保持するよりも、どのように多かれ少なかれ強力なのでしょうか? それとも、これは「複数の方法で実行できる」別のケースですか?
アルパン
C-std セクション 6.7.2.1 から引用すると、
struct s { int n; double d[]; };
これは有効な構造宣言です。この種の構文の実用的な使用法を探しています。正確に言えば、この構造は double* を 2 番目の要素として保持するよりも、どのように多かれ少なかれ強力なのでしょうか? それとも、これは「複数の方法で実行できる」別のケースですか?
アルパン
C FAQは、この質問に正確に答えます。簡単な答えは、この構造体には、double
構造体の外側の配列へのポインターではなく、構造体の内側に配列が含まれるということです。簡単な例として、次の例のように構造を使用できます。
struct s mystruct = malloc(sizeof(struct s) + 5 * sizeof(double));
s.n = 12;
s.d[0] = 4.0;
s.d[1] = 5.0;
s.d[2] = 6.0;
s.d[3] = 7.0;
s.d[4] = 8.0;
などなど - 気になる配列のサイズは割り当てに含まれており、それを任意の配列と同じように使用できます。通常、このような型には構造の一部としてサイズが含まれます。これ+
は、型の配列をスキップするトリックを使用するs
と、この状況では必然的に複雑になるためです。
追加の質問「[ポインター]を2番目の要素として保持するよりも、この構造はどのように多かれ少なかれ強力ですか?」に対して、それ自体はそれほど強力ではありませんが、ポインターを保持する必要はないので、保存します少なくともそれだけのスペース-構造をコピーするときも、配列へのポインターではなく配列をコピーします-微妙な違いがある場合もありますが、非常に重要な場合もあります。「複数の方法でそれを行うことができる」はおそらく良い説明ですが、特定のデザインまたは別のデザインが必要な場合があります.
主な利点は、柔軟な配列メンバーを使用すると、構造体内の他のデータと一緒に単一のメモリブロックを配列に割り当てることができることです(ポインタを使用すると、通常、2つの別々に割り当てられたブロックになります)。
また、かなりの数のネットワークプロトコルによって送信されるデータでも役立ちます。この場合、着信ストリームは同じ方法で定義されます。長さを定義する整数の後に、その数のデータ単位(通常はバイト/オクテット)が続きます。(通常)型のパンニングを使用して、柔軟な配列メンバーを持つ構造体をそのようなデータで満たされたバッファーにオーバーレイし、それを解析して個別に処理する代わりに、直接操作できます。
これを使用して、動的に割り当てられた配列にヘッダー フィールドを追加できます。最も一般的なのはそのサイズです。
struct int_array
{
size_t size;
int values[];
};
struct int_array *foo = malloc(sizeof *foo + 42 * sizeof *foo->values);
foo->size = 42;
...
for(size_t i = 0; i < foo->size; ++i)
foo->values[i] = i * i;
代わりにメンバーを使用し、配列を個別に割り当てることで同様の結果を得ることができますがint *
、メモリ (追加のポインター、2 番目のメモリ ブロックのヒープ管理) とランタイム (追加の間接化、2 番目の割り当て) の両方の点で効率が低下します。
長さでタグ付けされた文字列に Windows で使用されているのを見たことがあります。文字データは、長さの直後にメモリに保存され、すべてがきちんとまとめられます。
typedef struct {
SIZE_T bytes;
TCHAR chars[];
} tagged_string;