似たような質問がたくさんありますが、それでもC99/C11の可変長配列の機能に関連する答えは見つかりませんでした。
多次元可変長配列をC99/C11の関数に渡す方法は?
例えば:
_void foo(int n, int arr[][]) // <-- error here, how to fix?
{
}
void bar(int n)
{
int arr[n][n];
foo(n, arr);
}
_
コンパイラ(_g++-4.7 -std=gnu++11
_)言います:
_error: declaration of ‘arr’ as multidimensional array must have bounds for all dimensions except the first
_
それを_int *arr[]
_に変更しても、コンパイラーはまだ文句を言います:error: cannot convert ‘int (*)[(((sizetype)(((ssizetype)n) + -1)) + 1)]’ to ‘int**’ for argument ‘2’ to ‘void foo(int, int**)’
次の質問、値で渡す方法と参照で渡す方法は?どうやら、通常、配列を関数に渡すときに配列全体をコピーすることは望ましくありません。
定数の配列では、「定数」が示すように、関数を宣言するときに長さを知っている必要があるため、単純です。
_void foo2(int n, int arr[][10]) // <-- ok
{
}
void bar2()
{
int arr[10][10];
foo2(10, arr);
}
_
このような関数に配列を渡すことはベストプラクティスではなく、まったく気に入らないことは承知しています。おそらく、フラットポインター、オブジェクト(std:vectorなど)、またはその他の何らかの方法を使用した方がよいでしょう。しかし、それでも、私は理論的な観点からここでの答えは何なのか好奇心が強いです。
CとC++では、関数に配列を渡すのは少しおかしいです。配列型の右辺値はないため、実際にはポインターを渡します。
2D配列(配列の配列ではなく、実際の配列)をアドレスするには、2つのチャンクのデータを渡す必要があります。
そしてこれらは、C、C++、VLAあり、なし、またはその他の2つの別個の値です。
最もシンプルで、どこでも動作しますが、より多くの手作業が必要です
void foo(int width, int* arr) {
arr[x + y*width] = 5;
}
VLA、標準C99
void foo(int width, int arr[][width]) {
arr[x][y] = 5;
}
VLA w /逆引き引数、フォワードパラメーター宣言(GNU C拡張)
void foo(int width; int arr[][width], int width) {
arr[x][y]=5;
}
C++ w/VLA(GNU C++拡張、ひどくい)
void foo(int width, int* ptr) {
typedef int arrtype[][width];
arrtype& arr = *reinterpret_cast<arrtype*>(ptr);
arr[x][y]=5;
}
2D配列の[x] [y]表記は、配列の型に幅が含まれているため機能します。 VLAなし=配列型はコンパイル時に修正する必要があります。
したがって:VLAを使用できない場合は...
VLA(C99またはGNU C++拡張))を使用できる場合、...
C++の場合、boost::multi_array
は確実な選択です。
2D配列の場合、2つの個別の割り当てを行うことができます。
T
(A)へのポインターの1D配列T
の2D配列(B)次に、(A)のポインターを(B)の各行を指すように設定します。
このセットアップでは、(A)を単純なT**
そして[x][y]
インデックス作成。
このソリューションは2Dには適していますが、高次元にはますます多くの定型文が必要です。また、追加の間接層があるため、VLAソリューションよりも低速です。
また、B
の行ごとに個別の割り当てを行う同様のソリューションに遭遇する場合があります。Cでは、これはmalloc-in-a-loopのように見え、C++のvector-of-vectorsに類似しています。 。ただし、これにより、配列全体を1つのブロックにする利点がなくなります。
これを行う明確な方法はありませんが、回避策を使用して2次元配列を1次元配列として扱い、関数内で2次元配列に再変換できます。
void foo2(int n, int *arr)
{
int *ptr; // use this as a marker to go to next block
int i;
int j;
for(i = 0; i < n; i++)
{
ptr = arr + i*n; // this is the starting for arr[i] ...
for (j = 0; j < n ;j++)
{
printf(" %d ", ptr[j]); // This is same as arr[i][j]
}
}
}
void bar2()
{
int arr[10][10];
foo2(10, (int *)arr);
}