14

柔軟な配列メンバーを持つ構造体は、明らかに宣言することを意図したものではなく、その構造体へのポインターと組み合わせて使用​​ することを意図しています。柔軟な配列メンバーを宣言するときは、少なくとも 1 つの他のメンバーが必要であり、柔軟な配列メンバーはその構造体の最後のメンバーでなければなりません。

次のようなものがあるとしましょう:

struct example{
    int n;
    int flm[]; 
}

それを使用するには、ポインタを宣言し、malloc を使用して構造体の内容用にメモリを予約する必要があります。

struct example *ptr = malloc(sizeof(struct example) + 5*sizeof(int)); 

つまり、flm[] 配列に 5 つの整数を保持させたい場合です。次に、次のように構造体を使用できます。

ptr->flm[0] = 1; 

私の質問は、これの代わりにポインターを使用できるべきではないですか? C99 以前と互換性があるだけでなく、その構造体へのポインターの有無にかかわらず使用できます。私はすでに flm で malloc を使用しなければならないことを考えると、これを行うだけでよいのではないでしょうか?

example struct のこの新しい定義を考えてみましょう。

struct example{
    int n; 
    int *notflm; 
}

struct example test = {4, malloc(sizeof(int) * 5)}; 

柔軟な配列メンバーと同じ方法で置換を使用することもできます。

これも機能しますか?(notflm を使用した例の上記の定義を提供)

struct example test; 
test.n = 4; 
notflm = malloc(sizeof(int) * 5); 
4

2 に答える 2

26

ポインターは配列ではありません。どちらを使用するかを選択する基本的な理由は、配列とポインターの場合と同じです。柔軟な配列メンバーの特殊なケースでは、ポインターよりも柔軟な配列メンバーを好む理由がいくつかあります。

  • ストレージ要件の削減。ポインターは (通常) 4 バイトまたは 8 バイトだけ構造を拡大し、 を 1 回呼び出すのではなく、ポイント先のストレージを個別に割り当てると、オーバーヘッドがはるかに大きくなりますmalloc

  • アクセス効率の向上。柔軟な配列メンバーは、構造ベースから一定のオフセットに配置されます。ポインターには、別の逆参照が必要です。これは、それにアクセスするために必要な命令数とレジスタ プレッシャーの両方に影響します。

  • 割り当ての成功/失敗の原子性。構造体を割り当て、それが指すストレージを 2 つの別個のステップとして割り当てると、一方が成功し、もう一方が失敗した場合があるため、失敗した場合にクリーンアップするためのコードは非常に見苦しくなります。これは、ポインター演算を使用して同じmalloc要求から両方を分割することで回避できますが、アライメントの問題により、ロジックを間違えて UB を呼び出すのは簡単です。

  • ディープコピーの必要性を回避します。ポインターの代わりに柔軟な配列を使用する場合は、単純に memcpy (代入は柔軟な配列の長さを認識できないため、代入ではありません) で構造体をコピーできます。ポイント先のデータもコピーしてポインターを修正する必要はありません。新しいコピーで。

  • ディープフリーの必要性を回避します。ポイント先のデータもfree必要なく、単一のオブジェクトだけを使用できるのは非常に便利でクリーンです。freeもちろん、これは上記の「単一mallocの分割」アプローチでも実現できますが、柔軟な配列を使用すると、より簡単になり、エラーが発生しにくくなります。

  • 確かにもっと多くの理由...

于 2013-08-01T18:35:59.593 に答える
0

あなたが指摘したように、これらの概念は絶対に必要ではありません。

あなたが実証した2つの違いは、データがメモリ内にある場所です。

柔軟な配列を使用した最初の例では、メタデータと配列自体が同じメモリ ブロックにあり、必要に応じて 1 つのブロック (ポインター) として移動できます。

2 番目の例では、メタデータはスタック上にあり、配列はヒープ上の別の場所にあります。移動/コピーするには、メモリの 2 つのブロックを移動し、メタデータ構造のポインターを更新する必要があります。

通常、柔軟なサイズの配列は、配列とそのメタデータを空間的に一緒にメモリに配置する必要がある場合に使用されます。

これが確実に役立つ例は、たとえば、ファイルにメタデータを含む配列を配置する場合です。メモリの連続ブロックは 1 つしかなく、それをロードするたびに (ほとんどの場合) VM の別の場所に配置されます。 .

于 2013-08-01T18:36:09.210 に答える