array:array[2][4]
があり、メイン メソッド内に function の呼び出しがあるとしますblackandwhite
。このメソッドに配列の長さと幅を引数として渡すにはどうすればよいですか?
3 に答える
これは可能な解決策です:
void blackandwhite(int* array, int height, int width)
{
// Array-processing done here.
// array is pointer to int,
// initially points to element myarray[0][0].
// variable height = 2;
// variable width = 4;
}
int main()
{
int myarray[2][4];
blackandwhite(&myarray[0][0], 2, 4);
}
配列のサイズ、つまり要素の数は、次の構文で確認できます。
int array[8];
int size = sizeof(array)/sizeof(array[0]);
残念ながら、C 配列はネイティブ配列であり、メタデータが埋め込まれていません。行と列は、メモリ内の本質的に線形のストレージ スペースを表す/アクセスする方法にすぎません。私の知る限り、2D配列へのポインタが与えられた場合(Cで)、2D配列の行/列の数を自動的に決定する方法はありません。
したがって、上記の例に示すように、2D 配列へのポインターと共に、列/行の数を個別の引数として渡す必要があります。
関連する質問の詳細については、こちらをご覧ください。
アップデート:
一般的な落とし穴 1 : int** array
param-list で
の使用
整数の 2 次元配列へのポインターは、依然として int へのポインターであることに注意してください。
int**
param が int へのポインターへのポインターを参照していることを意味しますが、ここではそうではありません。
一般的な落とし穴 2 : int[][]
param-list で
の使用 配列の次元を渡せない。配列の 1 番目の次元のサイズを渡す必要はありません (ただし、渡すことはできますが、コンパイラはそれを無視します)。ただし、末尾の寸法は必須です。そう、
// is INVALID!
void blackandwhite(int array[][], int height, int width)
// is VALID, 2 is ignored.
void blackandwhite(int array[2][4], int height, int width)
// is VALID.
void blackandwhite(int array[][4], int height, int width)
C99 コンパイラ、または可変長配列をサポートする C2011 コンパイラを使用している場合は、次のようなことができます。
/**
* the cols parameter must be declared before it is used in
* the array parameter.
*/
void blackandwhite(size_t rows, size_t cols, int (*array)[cols])
{
size_t i, j;
for (i = 0; i < rows; i++)
for (j = 0; j < cols; j++)
array[i][j] = ...;
}
そして、あなたはそれを次のように呼びます
int array[N][M];
...
blackandwhite(N, M, array);
ほとんどの場合1、型 "N-element array of T
" の式は型 "pointer to T
" の式に変換 ("decay") され、式の値は最初の要素のアドレスになります。配列。式のarray
型は " N
-element array of M
-element array of int
"; にパラメーターとして渡すと、「またはの要素配列blackandwhite
へのポインター」型の式に変換され、その値は と同じです。M
int
int (*)[M]
&array[0]
可変長配列をサポートしていないC2011 コンパイラ(VLA サポートは現在オプションです)、または C89 以前のコンパイラ (VLA をサポートしていません) を使用している場合、2 つの選択肢があります。列:
void blackandwhite(size_t rows, int (*array)[M])
{
size_t i,j;
for (i = 0; i < rows; i++)
for (j = 0; j < M; j++)
array[i][j] = ...;
}
...
blackandwhite(N, array);
その場合、この関数は特定の列数を持つ配列でのみ機能します。または、配列の最初の要素へのポインターをパラメーターとして行と列と共に明示的に渡す theCodeArtist で示されているアプローチを使用することもできます。ただし、これはarray
、2D 配列ではなく 1D 配列として扱うことを意味します。つまり、2D インデックスを 1D 構造にマップする必要があります。
void blackandwhite(int *array, size_t rows, size_t cols)
{
size_t i, j;
for (i = 0; i < rows; i++)
for (j = 0; j < cols; j++)
array[i * rows + j] = ...; // use a 1D index
}
...
blackandwhite(&array[0][0], N, M);
このアプローチは、すべての行と列がarray
メモリ内で連続していることに依存していることに注意してください。array
そのように動的に割り当てた場合
int **array = malloc(N * sizeof *array);
if (array)
{
size_t i;
for (i = 0; i < N; i++)
array[i] = malloc(M * sizeof *array[i]);
}
行がメモリ内で連続して配置されるという保証はありません。ただし、この特定のケースでは、次のように記述できます。
void blackandwhite(int **array, size_t rows, size_t cols)
{
size_t i, j;
for (i = 0; i < rows; i++)
for (j = 0; j < cols; j++)
array[i][j] = ...;
}
...
blackandwhite(array, N, M);
まだ混乱していますか?
式は;a[i]
として扱われることに注意してください。*(a + i)
つまり、次の要素のアドレスを見つけてi
、結果a
のポインター値を逆参照します 。a[i][j]
として解釈され*(*(a+i)+j)
ます。 次の' 番目の配列*(a + i)
が得られます。上記の規則により、この式は配列型からポインター型に変換され、値は最初の要素のアドレスになります。次に、新しいポインター値に 追加し、結果を再度逆参照します。i
a
j
array[i][j]
wherearray
を配列へのポインター (int (*array)[cols]
またはint (*array)[M]
) またはポインターへのポインター ( ) として使用できます。int **array
これは、 の結果がarray[i]
ポインター型またはポインター型に崩壊する配列型のいずれかであり、添字演算子を持つことができるためです。それに適用されます。array[i][j]
wherearray
を への単純なポインターとして渡すことはできません。int
その場合、 の結果はarray[i]
ポインター型ではないためです。
では、なぜしないのですか
void blackandwhite(int **array, size_t rows, size_t cols) {...}
...
int array[N][M];
blackandwhite((int **) array, N, M);
ポインターと配列は別のものなので、ポインターへのポインターと配列へのポインターは別のものです。それはおそらくうまくいくでしょうが、あなたはコンパイラに嘘をついています。何よりもフォームが悪い。
編集
関数パラメーター宣言のコンテキストでは、T a[]
or形式のパラメーター宣言は;T a[N]
として解釈されます。T *a
IOWa
は、配列ではなくポインターとして宣言されます。同様に、フォームT a[N][M]
orのパラメーター宣言は;T a[][M]
として扱われます。T (*a)[M]
繰り返しa
ますが、配列ではなくポインタとして宣言されています。
最初の例では、次のように宣言できblackandwhite
ました。
void blackandwhite(size_t rows, size_t cols, int array[rows][cols])
また
void blackandwhite(size_t rows, size_t cols, int array[][cols])
しかし、何が起こっているのかを正しく反映しているため、ポインター宣言を明示的に使用することを好みます。
1. この規則の例外は、配列式が
sizeof
、_Alignof
、または単項演算子のオペランドである場合、または次&
のような宣言で別の配列を初期化するために使用される文字列リテラルである場合です。
char str[]="This is a test";
.
C では、文字列の長さを計算する方法があることが知られていますが、配列の場合は、関数に長さと幅を渡すために配列の次元を明示的に指定する必要がありますblackandwhite
。