連続したメモリ (別の 5D 配列) に読み込む必要がある非常に大きな 5D 配列を使用しています。配列が大きすぎてセグ フォールトが発生するため、配列をスタックに配置できません。私が行ったことは、malloc を使用して 5D 配列を動的に作成することですが、連続したメモリではないことがわかりました。これに対するエレガントな解決策はありますか、それとも面倒なことになるのでしょうか?
6 に答える
Jens Gustedt から:偽の行列を使用しないでください。
次のように、次元 A x B x C x D x E (次元はコンパイル時に既知である必要はありません) を持つ 5 次元行列を割り当てます。
float (*matrix5d)[B][C][D][E] = malloc(sizeof(float[A][B][C][D][E]));
free を 1 回呼び出すだけでメモリを解放します。
free(matrix5d);
上記では、可変長配列には C99 以降が必要であることに注意してください。
これについて考える 1 つの方法は、基本的に 1 次元配列のみを割り当てることができ、N 次元配列は (N-1) 次元配列の 1 次元malloc
配列であるため、4 次元配列の 1 次元配列を割り当てるために使用することです。malloc
ただし、 によって割り当てられる配列と同様malloc
に、「配列オブジェクト」は実際にはポインターであるため、 を使用sizeof()
して配列のサイズを取得しないでください。
#include <stdio.h>
#include <stdlib.h>
typedef int Array_4D_Type[4][3][2][1];
int main(void) {
Array_4D_Type *arr = malloc(5 * sizeof(Array_4D_Type));
// ^^^^^^^^^^^^^^^^ here, allocate a length-5 vector of 4d array type
int *p = &arr[0][0][0][0][0];
for (int i = 0 ; i < 120 ; i++){
p[i] = i;
}
printf("arr_start = %d, end = %d\n", arr[0][0][0][0][0], arr[4][3][2][1][0]);
return 0;
}
アップデート:
コメントに記載されているように、typedef
ここで使用すると、配列は最上位の次元を除いて静的なサイズになります。
ここでの使用は、typedef
配列へのポインタの構文を少しきれいにするためだけです。
ただし、VLA が有効になってint (*arr)[n][o][p][q] = malloc(m*sizeof(*arr));
いる場合でも機能し、各ディメンションで動的サイズを指定できます。
メモリを連続させる方法はありますが、エレガントか乱雑かはあなた次第です ;)
まず、1 次元配列の場合を考えてみましょう。この場合、連続したメモリを取得するのは簡単です。取得するメモリはmalloc
連続します。単純に思えますが、後でこの事実を使用して 5 次元の連続配列を取得します。
M
次に、サイズがbyの 2 次元配列を考えてみましょうN
。これを作成する 1 つの方法を次に示します ( float
s を使用していると仮定します)。
float** array2d = malloc(M * sizeof(float*));
for (int i = 0; i < M; i++) {
array2d[i] = malloc(N * sizeof(float));
}
厳密に言えば、これは2 次元配列ではなく、配列の配列です。これで、array2d
likeなどの要素にアクセスできます。概念的にはこれは非常に優れていますが、お気づきのように、 を複数回呼び出したので、必ずしも連続したメモリがあるとは限りません。必要なのは、float を格納するために必要なすべてのメモリを 1 回の呼び出しで割り当てる方法です。array2d[0][0]
array2d[0][1]
malloc
M*N
malloc
float* array2d = malloc(M * N * sizeof(float));
この形式でarray2d
は、 はfloat*
ではなくfloat**
、つまり、float の配列の配列ではなく、float の配列であることに注意してください。ですから、これ以上はできませんarray2d[0][0]
。この配列にどのようにインデックスを付けますか?
この 2 次元配列をメモリ内でどのようにレイアウトするかは、完全に私たち次第です。M
それが配列の「幅」(行の要素数を意味する)であり、それN
が配列の「高さ」(配列の行数を意味する)であるとしましょう。M
また、配列の最初のエントリが最初の行であり、次のエントリが 2 番目の行であるとだけ言っておきましょうM
。したがって、行y
、列のエントリを読み取るには、次のx
ようにします。
float data = array2d[y * M + x];
要素 (0, 0) が必要だとします。その後y * M + x
、単に 0 になるので、問題ありません。ここで、要素 (1, 0) (つまり、2 行目の最初の要素) が必要だとします。上で決定したように、これy * M + x
がM
2 番目の行の開始点になります。
このアプローチを高次元に一般化できます。サイズL
が byの 3 次元配列があるとM
しN
ます。これは、すべてのサイズがL
のメモリ内に順番に配置された 2 次元配列と考えることができます。次に、要素 ( 、、) にアクセスするには、次のようにします。M
N
x
y
z
float data = array3d[z * (M * N) + y * (M) + x];
z
概念的には、これは最初の2 次元配列をスキップし、次にy
その配列の最初の行をスキップして、x
その行の th 要素に移動すると考えることができます。次元が増えると、索引付け時に乗法項が増えますが、アプローチは基本的に同じです。
動的割り当てでは、malloc を使用します。
int** x;
x = malloc(dimension1_max * sizeof(int*));
for (int i = 0; i < dimension1_max; i++) {
x[i] = malloc(dimension2_max * sizeof(int));
}
[...]
for (int i = 0; i < dimension1_max; i++) {
free(x[i]);
}
free(x);
これにより、サイズが dimension1_max * dimension2_max の 2D 配列が割り当てられます。したがって、たとえば、640*480 配列 (イメージの fe ピクセル) が必要な場合は、dimension1_max = 640、dimension2_max = 480 を使用します。その後、x[d1][d2] (d1 = 0) を使用して配列にアクセスできます。 639、d2 = 0..479。
ただし、SOまたはGoogleで検索すると、他の可能性も明らかになります。たとえば、このSOの質問
その場合、配列はメモリの連続した領域 (640*480 バイト) を割り当てないことに注意してください。これを前提とする関数で問題が発生する可能性があります。したがって、条件を満たす配列を取得するには、上記の malloc ブロックを次のように置き換えます。
int** x;
int* temp;
x = malloc(dimension1_max * sizeof(int*));
temp = malloc(dimension1_max * dimension2_max * sizeof(int));
for (int i = 0; i < dimension1_max; i++) {
x[i] = temp + (i * dimension2_max);
}
[...]
free(temp);
free(x);
同様の方法で、動的に 5d 配列を構築できます