関数の外部で宣言された可変サイズ型
-
07-07-2019 - |
質問
2次元配列を宣言するとき
int random[height][width];
そしてそれを関数で使用する
void populate(int random[height][width], int x, int y)
関数の外部で宣言されたエラー可変サイズ型を提供します。 私は何か間違ったことをしていること、そしてそれが小さいことを知っています。記憶力が悪い...
解決
今、ステップアップして、多次元配列はCやC ++での脳の努力に値しないことを伝えます。単次元配列(または標準コンテナー)を使用して、インデックス関数を作成する方がはるかに優れています。
inline int index (int x, int y)
{
return x + y * width;
}
これで問題が発生しました。 C ++は、C99可変長配列をサポートしていません。コンパイラは、コンパイル時に配列のサイズを知っている必要があります。たとえば、次は機能しません。
int dim = 4;
int ar[dim];
dim
が const
の場合、コンパイラーは ar
の幅を正確に知ることができるため、動作します(値のため) dim
は変更されません)。これはおそらくあなたが遭遇している問題です。
コンパイル時にサイズを変更できるようにするには、テンプレート化された参照を書くなど、もっと難しいことをする必要があります。 C / C ++でのレイアウト方法のため、多次元配列にポインターを使用することはできません。テンプレート化された例は、次の異常のように見える場合があります。
template <int Width, int Height>
void populate(int (&(&random)[Width])[Height], int x, int y);
これはいです。
実行時には、 new
を使用してデータを割り当てるか、コンテナタイプを使用する必要があります。
他のヒント
関数の外側、つまりスタックフレーム内ではない一定の次元(幅、高さ)で配列を定義することはできません。次元はコンパイル時に不明であるためです。定数を使用するか、動的に割り当てます(ヒープまたはスタックフレームのいずれか)。
配列がパラメーターとして関数に直接渡される場合(値渡し)、配列の最初の要素へのポインターに減衰します。署名で配列の次元を明確に読み取ることができたとしても、それらの次元はコンパイラによって無視されます。この動作はCと互換性があります。
C ++を使用すると、参照によって配列を渡すことができ、それはもう問題にはなりません。
int extract_value( int (&a)[10][10], int row, int col ) {
return a[row][col];
}
int main() {
int a[10][10] = {};
a[5][5] = 1;
std::cout << extract_value( a, 5, 5 ) << std::endl;
int b[5][5];
// extract_value( b, 2, 2 ); // error: the function takes an array of 10x10
}
関数パラメータは正確に一致する必要があります。つまり、10x10要素の配列のみを受け取ります。関数を配列サイズにテンプレート化することにより、その制限を取り除くことができます。また、次のように入力します:
template <typename T, int Rows, int Cols>
T extract_value( T (&a)[Rows][Cols], int row, int col ) {
return a[row][col];
}
int main() {
int a[5][7] = {};
extract_value( a, 3, 4 );
int b[8][2] = {};
extract_value( b, 7, 1 ); // correct, the compiler matches sizes
double c[4][4] = {};
extract_value( c, 2, 2 ); // different types are allowed
}
この解決策は、サイズがコンパイル時定数でなければならず、配列がスタックに割り当てられなければならないという点で、依然として面倒です。これに対する解決策は、以前に提案されたように、バッファ内の動的メモリ(線形)を取得し、値を取得するためにN座標系から1次元配列に変換するクラスを定義することです。こので、その実行方法に関するヒントを入手できます。 2Dマトリックスの実装を提供する演算子のオーバーロードに関するFAQ 。実装したら、それを関数/メソッドのパラメーターとして使用できます。
この最後のパスに従うことをお勧めします:N次元配列を、1Dベクトルへの変換を提供するクラスにカプセル化します(C ++ FAQ liteは生のポインターを使用します。STLコンテナーを好みます)。
例で説明します:
// globals
const int ARRAY_SIZE = 16
struct ArrayType_t arrayType [ARRAY_SIZE];
ARRAY_SIZEは定数intとして宣言されていますが、その値はコンパイル時に初期化されないため、コンパイラーは配列のサイズを認識せず、そのようなエラーが発生します。ただし、ハッシュとして定義する場合
#define ARRAY_SIZE 16
struct ArrayType_t arrayType [ARRAY_SIZE]
===&gt; ARRAY_SIZEが定義されているため、これは機能します
コンパイル時に、コンパイラはコンパイル時に配列のサイズを知ることができます。
次のようなものを使用できます:
void populate(int height, int width, int **random)
{
//here you can work from random[0][0] to random[height][width]
}
次のように使用できます:
int main()
{
int height=10;
int width=20;
int **myarray = new int*[height];
for( int i=0; i< height; i++ ) myarray[i] = new int[width];
populate( height, width, myarray);
}
しかし、もちろん、バッファオーバーフローに注意する必要があります