Kochen の「Programming in C」という本を読んでいるのですが、多次元配列の初期化について彼が説明している箇所がわかりません。
特に、次の文の意味がわかりません。この場合、正しい初期化を強制するために中括弧の内側のペアが必要であることに注意してください。それらがなければ、最初の 2 行と 3 行目の最初の 2 つの要素が代わりに初期化されます。文が一体何を意味するのかわかりません。
Kochen の「Programming in C」という本を読んでいるのですが、多次元配列の初期化について彼が説明している箇所がわかりません。
特に、次の文の意味がわかりません。この場合、正しい初期化を強制するために中括弧の内側のペアが必要であることに注意してください。それらがなければ、最初の 2 行と 3 行目の最初の 2 つの要素が代わりに初期化されます。文が一体何を意味するのかわかりません。
これは、M[4][5]
配列に 20 の要素 (4 行、5 列) があり、内部の中かっこのペアを使用して行が明示的に指定されていない場合、初期化の既定の順序が行ごとであるためです。
これが意味することは、単純な線形リストとして同じ 12 個の値を割り当てる場合、内側の中括弧のペアなしで、最初の 2 行 (2*5 = 10 要素) と 3 番目の最初の 2 列に値が割り当てられることです。行。(明示的に初期化しなかった配列の残りの 8 つの要素は、自動的に 0 に設定されます。)
C コンパイラは、各行に 5 列しかないことを認識しており、5 列のマージンに達するたびに、数値のリストを次の行に自動的にラップします。したがって、
int M[4][5] = {10, 5, -3, 9, 0, 0, 32, 20, 1, 0, 0, 8};
意味すると理解される
int M[4][5] =
{
{10, 5, -3, 9, 0},
{ 0, 32, 20, 1, 0},
{ 0, 8, 0, 0, 0},
{ 0, 0, 0, 0, 0}
};
内側の中かっこを使用して 12 個の値を好みの行に分けることで、既定の順序をオーバーライドできます (ただし、配列のこの定義では、1 行あたり 5 列を超えないようにしてくださいM
)。
たとえば、本のページのように、同じ 12 個の値を 3 の 4 つのセットに分割するために内側のブレースを使用する場合、これらの内側のブレースは多次元配列の個別の行を初期化するために解釈されます。その結果、配列の4 行が初期化されますが、それらの 4 行の最初の 3 列のみが初期化され、残りの列はゼロに設定されます (各行の最後にある 2 つの空白のゼロ値)。
つまり、C コンパイラは、配列のM
各行に 5 つの列があることを認識しているため、欠落している列を各行に追加して、各行に 5 つの列があるため、配列には合計 20 の値が含まれます。 :
int M[4][5] =
{
{10, 5, -3},
{ 9, 0, 0},
{32, 20, 1},
{ 0, 0, 8}
};
意味すると理解される
int M[4][5] =
{
{10, 5, -3, 0, 0},
{ 9, 0, 0, 0, 0},
{32, 20, 1, 0, 0},
{ 0, 0, 8, 0, 0}
};
内側のブレースを使用すると、配列は次のようになります。
10 5 -3 0 0
9 0 0 0 0
32 20 1 0 0
0 0 8 0 0
したがって、すべての行で、最後の 2 つの値はゼロです (値を設定していないためです。内側の波括弧がないと、配列は次のようになります。
10 5 -3 9 0
0 32 20 1 0
0 8 0 0 0
0 0 0 0 0
最初の 12 要素のみが指定された値になり、残りは 0 になります。
すべての配列は内部的に 1 次元配列として動作するため、どの行を正確に初期化するかを括弧で指定する必要があります。
例えば:
int a[4][5] = {
{ 1, 2, 3 }, // those are the elements of the first row.
// only the first 3 elements are initialized
{ 1, 2, 3, 4} // those are the elements of the 2nd row.
// only the first 4 element are initialized
};
// everything else will be equal to 0
その間
int a[4][5] = { 1, 2, 3, 1, 2, 3, 4}; // this will initialize first 5 elements
// of the first row and then continue
// with the 2nd one making the first 2
// elements to be 3 and 4 respectivly
// everything else will be equal to 0
C の多次元配列は、1 次元配列の「シンタックス シュガー」にすぎません。4 x 5 の int 配列を割り当てると、メモリ内の行に 20 個の整数のスペースが実際に割り当てられます。これらの整数は、最初の行のすべての要素、次に 2 番目の行のすべての要素、というように格納されます。
内側の中かっこがない場合、イニシャライザも 1D であり、これら 20 個の整数のうち最初の 12 個、つまり最初の 2 行と 3 行目の最初の 2 つの要素を初期化することを示します。
具体例を見ると参考になります。
多次元配列は配列の配列です。(これは、長い 1 次元配列の単なるシンタックス シュガーではありません。)
初期化子では、末尾の要素 (暗黙的にゼロに初期化される) と内側の中括弧の両方を省略できます。
与えられた:
int arr[2][2];
完全な初期化は次のようになります。
int arr[2][2] = { { 10, 20 }, { 30, 40 } };
内側のブレースを省略できます (ただし、IMHO は省略しないでください)。
int arr[2][2] = { 10, 20, 30, 40 };
コンパイラは、初期化子の要素を の要素にマップしますarr
。
末尾の要素を省略した場合:
int arr[2][2] = { { 10, 20 } };
次に、2 番目の行が暗黙的に に初期化され{ 0, 0 }
ます。
または、次のように書くこともできます。
int arr[2][2] = { { 10 }, { 20 } };
これにより、値 10 と 20 が最初の行ではなく、各行の最初の要素に割り当てられます。
繰り返しますが、例を見ずに作者が何について話しているのかを正確に伝えるのは困難ですが、最初の行が不完全であっても、内側のブレースはコンパイラに新しい行を開始するように指示します。
4 つの要素すべて (より一般的にはすべての要素) に初期化子を指定する場合X * Y
、内側の波括弧は厳密には必要ありません。要素の順序はどちらの方法でも同じです。
個人的には、初期化する実際の構造を反映しているため、とにかくすべての内側のブレースを含める方がはるかに明確だと思います。
(では、1 次元配列と 2 次元配列の違いは、シンタックス シュガーを除いて何ですか? 上記の の宣言を考えると、arr
それが 1 次元配列と同じである場合、arr[0][2]
は と同じになります。arr[1][0]
インデックスが 2 番目の行にオーバーフローします. 実際にはそのように機能するかもしれませんが, 実際にarr[0][2]
は未定義の動作があります. これには実際的な結果があります. 最適化コンパイラはすべての境界が範囲内にあると仮定し, その仮定に違反した場合に誤動作するコードを生成する可能性があります. .)
この質問も参照してください。