このイディオムは当然1D配列の割り当てから外れます。まず、任意のタイプの1D配列を割り当てることから始めましょうT
。
T *p = malloc( sizeof *p * N );
シンプルでしょ?式は *p
型を持つT
ので、sizeof *p
同じ結果になりますsizeof (T)
ので、我々は十分なスペースを割り当てている、N
の-element配列T
。これはどのタイプT
にも当てはまります。
では、のT
ような配列型に置き換えましょうR [10]
。次に、割り当ては
R (*p)[10] = malloc( sizeof *p * N);
ここでのセマンティクスは、1D割り当て方法とまったく同じです。変更されるのはのタイプだけですp
。の代わりにT *
、今R (*)[10]
です。式*p
はtype T
である型を持っているR [10]
のでsizeof *p
、sizeof (T)
whichはwhichと同等sizeof (R [10])
です。したがってN
、の10
要素ごとの配列に十分なスペースを割り当てていますR
。
必要に応じて、これをさらに進めることができます。R
それ自体が配列型だとしましょうint [5]
。それを代用するとR
、
int (*p)[10][5] = malloc( sizeof *p * N);
同じ契約は- sizeof *p
と同じでありsizeof (int [10][5])
、我々は保持するのにメモリ十分な大きさの連続したチャンクを割り当てる羽目になるN
によって10
で5
の配列int
。
これが割り当ての側面です。アクセス側はどうですか?
[]
添え字演算はポインタ演算の観点から定義されていることを覚えておいてください:a[i]
は*(a + i)
1として定義されています。したがって、添え字演算子はポインターを[]
暗黙的に逆参照します。p
がへのポインタの場合T
、単項演算*
子を使用して明示的に逆参照することにより、ポイントされた値にアクセスできます。
T x = *p;
または[]
添え字演算子を使用して:
T x = p[0]; // identical to *p
したがって、が配列のp
最初の要素を指している場合、ポインターに添え字を使用することにより、その配列の任意の要素にアクセスできます。p
T arr[N];
T *p = arr; // expression arr "decays" from type T [N] to T *
...
T x = p[i]; // access the i'th element of arr through pointer p
ここで、置換操作を再度実行しT
て、配列型に置き換えR [10]
ます。
R arr[N][10];
R (*p)[10] = arr; // expression arr "decays" from type R [N][10] to R (*)[10]
...
R x = (*p)[i];
すぐに明らかな違いの1つ。p
下付き演算子を適用する前に明示的に逆参照しています。に添え字を付けるのではなくp
、何をp
指すのかを添え字にしたい(この場合は配列 arr[0]
)。単項*
は下付き[]
演算子よりも優先順位が低いため、括弧を使用して明示的にでグループ化p
する必要があり*
ます。ただし、上から*p
はと同じp[0]
であることを覚えておいてください。
R x = (p[0])[i];
あるいは単に
R x = p[0][i];
したがって、p
2D配列を指す場合、次のようにしてその配列にインデックスを付けることができますp
。
R x = p[i][j]; // access the i'th element of arr through pointer p;
// each arr[i] is a 10-element array of R
上記と同じ結論にこれを撮影し、置き換えR
てint [5]
:
int arr[N][10][5];
int (*p)[10][5]; // expression arr "decays" from type int [N][5][10] to int (*)[10][5]
...
int x = p[i][j][k];
これは、通常の配列を指す場合も、を介して割り当てられたメモリを指す場合も同じように機能p
しmalloc
ます。
このイディオムには次の利点があります。
- シンプルです-断片的な割り当て方法とは異なり、コードは1行だけです
T **arr = malloc( sizeof *arr * N );
if ( arr )
{
for ( size_t i = 0; i < N; i++ )
{
arr[i] = malloc( sizeof *arr[i] * M );
}
}
- 割り当てられた配列のすべての行は*連続*ですが、これは上記の段階的な割り当て方法の場合とは異なります。
- 配列の割り当て解除は、を1回呼び出すだけで簡単
free
です。繰り返しますが、断片的な割り当て方法では当てはまりません。この方法では、割り当てを解除するarr[i]
前に、それぞれの割り当てを解除する必要がありますarr
。
ヒープの断片化が激しく、メモリを連続したチャンクとして割り当てることができない場合や、各行の長さが異なる可能性のある「ギザギザ」の配列を割り当てる場合など、断片的な割り当て方法が望ましい場合があります。しかし、一般的には、これがより良い方法です。
1.配列はポインターではないことに注意してください。代わりに、配列式は必要に応じてポインター式に変換されます。