連続したメモリ領域を持つ多次元配列
通常、C言語/C++で多次元配列を作る時は
この方法はとても便利ですが、要素数は定数でなければならず、開発環境によっては大きなサイズの配列を作ることができません(https://msdn.microsoft.com/ja-jp/library/s0z0bbfe.aspx)。
これを解決するには、mallocなどで配列を動的に確保します。
先程の配列のアドレスを表示してみましょう。
原因は5行目でfor文を使って行インデックス別に列要素をmallocで確保しているためです。
メモリ領域を連続にするには、このように行列の全要素を確保した後でインデックスを与えます。
連続していることが確認できます。
おまけ
としますね。
// 2x3の配列
int a[2][3];
この方法はとても便利ですが、要素数は定数でなければならず、開発環境によっては大きなサイズの配列を作ることができません(https://msdn.microsoft.com/ja-jp/library/s0z0bbfe.aspx)。
これを解決するには、mallocなどで配列を動的に確保します。
普通に[]でアクセスするのは問題ありませんが、配列まるごとmemcpyしようとするとエラーが起きます。
#include <stdlib.h>
// 2x3の配列
int **b = (int**)malloc(2 * sizeof(int*));
for (int i = 0; i < 2; i++) b[i] = (int*)malloc(3 * sizeof(int*));
// 解放
for (int i = 0; i < 2; i++) free(b[i]);
free(b);
先程の配列のアドレスを表示してみましょう。
結果はこうなります。
// 表示
printf("b:\n");
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("%p\n", &b[i][j]);
}
}
printf("\n");
配列版 | malloc版 | ||
a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2] | 0141FF24 0141FF28 0141FF2C 0141FF30 0141FF34 0141FF38 | b[0][0] b[0][1] b[0][2] b[1][0] b[1][1] b[1][2] | 001C1A10 ←アドレスは 001C1A14 ←4バイトずつ 001C1A18 ←増える 001C1A28 ←アドレスのずれが発生 001C1A2C 001C1A30 |
原因は5行目でfor文を使って行インデックス別に列要素をmallocで確保しているためです。
メモリ領域を連続にするには、このように行列の全要素を確保した後でインデックスを与えます。
アドレスはこのように、
// メモリ領域が連続な2x3の配列
int **c = (int**)malloc(2 * sizeof(int*));
c[0] = (int*)malloc(2 * 3 * sizeof(int));
for (int i = 1; i < 2; i++) c[i] = c[i-1] + 3;
// 解放
free(c[0]);
free(c);
malloc改良版 | |
c[0][0] c[0][1] c[0][2] c[1][0] c[1][1] c[1][2] | 002A1A10 002A1A14 002A1A18 002A1A1C 002A1A20 002A1A24 |
連続していることが確認できます。
おまけ
3次元配列の場合はこのようにしましょう(「良いもの。悪いもの。」様の記事を参考)。
※2013/01/10
解放がfree()一つで済む実装が「和田維作のホームページ」様のTIPSに紹介されてあります。
最後に4次元配列版をどうぞ。
C#みたいにnew int[2][3]とかできたらいいのになぁ。
なかなかカオスな事になっていますが、4次元だとこうなります。
// メモリ領域が連続な2x3x4の配列
int ***d = (int***)malloc(2 * sizeof(int**));
d[0] = (int**)malloc(2 * 3 * sizeof(int*));
d[0][0] = (int*)malloc(2 * 3 * 4 * sizeof(int));
for (int i = 0; i < 2; i++) {
d[i] = d[0] + i * 3;
for (int j = 0; j < 3; j++) d[i][j] = d[0][0] + i * 3 * 4 + j * 4;
}
// 解放
free(d[0][0]);
free(d[0]);
free(d);
なんのこっちゃ。
// メモリ領域が連続な2x3x4x5の配列
int ****e = (int****)malloc(2 * sizeof(int***));
e[0] = (int***)malloc(2 * 3 * sizeof(int**));
e[0][0] = (int**)malloc(2 * 3 * 4 * sizeof(int*));
e[0][0][0] = (int*)malloc(2 * 3 * 4 * 5 * sizeof(int));
for (int i = 0; i < 2; i++) {
e[i] = e[0] + i * 3;
for (int j = 0; j < 3; j++) {
e[i][j] = e[0][0] + i*3*4 + j*4;
for (int k = 0; k < 4; k++) e[i][j][k] = e[0][0][0] + i*3*4*5 + j*4*5 + k*5;
}
}
// 解放
free(e[0][0][0]);
free(e[0][0]);
free(e[0]);
free(e);
※2013/01/10
解放がfree()一つで済む実装が「和田維作のホームページ」様のTIPSに紹介されてあります。
これは便利!
// メモリ領域が連続な2次元配列
void *malloc2d(size_t size, int row, int col)
{
char **a, *b;
int t = size * col;
// インデックスと要素を一気に確保
a = (char**)malloc((sizeof(*a) + t) * row);
if (a) {
// [インデックス, インデックス, ..., 要素, 要素, 要素, ...]
// と整列させるため要素の開始位置をずらす
b = (char*)(a + row);
// 各行の先頭アドレスを与える
for (int i = 0; i < row; i++) {
a[i] = b;
b += t; // 要素のサイズ×列の長さの分だけずらす
}
return a;
}
return NULL;
}
3次元配列版もあります。
int main(void)
{
// メモリ確保
int **f = (int**)malloc2d(sizeof(int), 2, 3);
// 表示
printf("f:\n");
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("%p\n", &f[i][j]);
}
}
printf("\n");
// 解放
free(f);
return 0;
}
なるほど、わからん。
// メモリ領域が連続な3次元配列
void *malloc3d(size_t size, int i, int j, int k)
{
char ***a, **b, *c;
int t = size * k;
// インデックスと要素を一気に確保
a = (char***)malloc((sizeof(*a) + sizeof(**a) * j + t * j) * i);
if (a) {
b = (char**)(a + i);
c = (char *)(b + i * j);
for (int idx1 = 0; idx1 < i; idx1++) {
a[idx1] = b;
for (int idx2 = 0; idx2 < j; idx2++) {
b[idx2] = c;
c += t;
}
b += j;
}
return a;
}
return NULL;
}
最後に4次元配列版をどうぞ。
書いてて発狂しそうになりました。パターンが分かれば簡単なのですが...
// メモリ領域が連続な4次元配列
void *malloc4d(size_t size, int i, int j, int k, int l)
{
char ****a, ***b, **c, *d;
int t = size * l;
// インデックスと要素を一気に確保
a = (char****)malloc((sizeof(*a) + sizeof(**a) * j + sizeof(***a) * j * k + t * j * k) * i);
if (a) {
b = (char***)(a + i);
c = (char **)(b + i * j);
d = (char *)(c + i * j * k);
for (int idx0 = 0; idx0 < i; idx0++) {
a[idx0] = b;
for (int idx1 = 0; idx1 < j; idx1++) {
b[idx1] = c;
for (int idx2 = 0; idx2 < k; idx2++) {
c[idx2] = d;
d += t;
}
c += k;
}
b += j;
}
return a;
}
return NULL;
}
C#みたいにnew int[2][3]とかできたらいいのになぁ。
- 関連記事
-
- CV Droneとフォームアプリ
- CV Droneとライセンス
- 連続したメモリ領域を持つ多次元配列
- Win32APIでスプラッシュウィンドウ
- 本当にあった怖いコード