sizeof
、_Alignof
、または単項演算子のオペランドである場合、または&
宣言で別の配列を初期化するために使用される文字列リテラルである場合を除いて、型 "N 要素配列 " の式T
は変換 ("decay") されます。タイプ「ポインター」の式T
であり、その値は配列の最初の要素のアドレスになります ( 6.3.2.1/3 )。
の行return hws;
でpopulate
、式の型hws
は「100 要素の配列の 20 要素の配列int
」です。上記の規則により、「の 20 要素配列へのポインタint
」、または型に変換されますint (*)[20]
。したがって、の正しい宣言は次のようにpopulate
する必要があります
int (*populate())[20]
{
...
return hws;
}
次のように読み取ります
populate -- populate
populate() -- is a function
*populate() -- returning a pointer
(*populate())[20] -- to a 20-element array
int (*populate())[20] -- of int.
int (*)[20]
また、結果を返すもののタイプも同様である必要があります。
そうは言っても...
このようにグローバル変数を使用することは、いくつかの理由から強くお勧めできません。populate
次のように、配列をパラメーターとして渡す方がよいでしょう。
void populate(int (*arr)[20], size_t rows)
{
size_t i, j;
for (i = 0; i < rows; i++)
{
for (j = 0; j < 20; j++)
{
arr[i][j] = randomize();
}
}
}
これを単に次のように呼び出します
populate(hws, sizeof hws / sizeof hws[0]);
可変長配列をサポートするコンパイラ (C99 コンパイラまたは__STDC_NO_VLA__
それを定義しないか 0 に定義する C2011 コンパイラ) を使用している場合は、関数を次のように定義できます。
void populate(size_t cols, size_t rows, int (*arr)[cols]) // cols must be declared
{ // before it can be used
size_t i, j; // to declare arr
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
arr[i][j] = randomize();
}
}
}
そしてそれを次のように呼び出します
size_t rows = sizeof hws[0] / sizeof hws[0][0]; // dividing the size of the array
size_t cols = sizeof hws / sizeof hws[0]; // by the size of the first element
populate(cols, rows, hws); // gives the number of elements
そのため、20 をどこにもハードコーディングしていません。
可変長配列をサポートするコンパイラを使用しておらず、関数プロトタイプの行数をハードコーディングしたくない場合は、次のようにすることができます。
void populate(int *arr, size_t rows, size_t cols)
{
size_t i, j;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
arr[i * rows + j] = randomize();
}
}
}
そしてそれを次のように呼び出します
// rows and cols calculated as above
populate(&hws[0][0], rows, cols);
この場合、配列へのポインターを渡す代わりに、最初の要素へのポインターを渡します (アドレス値は同じですが、型はint *
の代わりになりint (*)[20]
ます。したがって、populate
関数はそれを 1D 配列のように扱い、オフセットを計算しますこのトリックは、連続して割り当てられた 2D 配列に対してのみ機能することに注意しi * rows + j
てください。
populate
これは、Nx20 だけでなく、任意のサイズの配列で動作できること も意味します。