• ベストアンサー

3次元配列の動的確保

2次元配列は、 // 動的確保 int **mat = new int*[row]; for(i = 0; i < row; i++) mat[i] = new int[col]; //解放 for(i = 0; i < row; i++) delete [] mat[i]; delete [] mat; で、生成と開放はできたのですが、3次元となると、途端にわからなくなります。 かれこれ1時間半は試行錯誤はしているのですが、たどり着きそうにありません。 どなたかご教授願います。

質問者が選んだベストアンサー

  • ベストアンサー
  • ency
  • ベストアンサー率39% (93/238)
回答No.8

No6 ency です。 > delete[][] というのがミソでしょうか。 > 恥ずかしながらはじめて見ました。 lachesis-r さんはぜんぜん恥ずかしくないです。 逆に、恥ずかしいのは私です。。。 該当箇所は、 delete[] mat[i][j] で置き換えてください。 # 私もなんて恥ずかしいミスをしてしまったのか・・・。

lachesis-r
質問者

補足

ありがとうございます。 残念ながら、コンパイルできません。 試行錯誤してみたのですが、なかなかうまくいきませんでした。

その他の回答 (8)

  • Soli
  • ベストアンサー率11% (7/61)
回答No.9

No7です。 > 1次元配列に置き換えるということですね。 > これだと要素数が2のn乗でない場合には遅くなるかもしれませんね。 > (すみません。試していません) > クラスにして要素へのアクセスを[]演算子でできるようにして・・・という感じでしょうか? 1次元にすると各要素ごとのメモリ確保・解放が出来ないので 常に最大量食いっぱなしなのが美しくないですけどね^^; 処理が遅い、というのはアセンブラに直してみると解ると思いますが、 値を取るたびに乗算が複数回行われるので 3次配列に比べて遅いかなぁ、と思って書きました。 #すみません試してません… 2のn乗でない場合も遅いかもなんですね。 場合によって使い方はマチマチなのですが、 手っ取り早くそのまま使ってることが多いです。 色々端折ってますが以下のような感じです。 void CHogeHoge::Create3DArray( int nRangeX, int nRangeY, int nRangeZ ) { m_pBuffer = new int[ nRangeX * nRangeY * nRangeZ ]; m_nRangeX = nRangeX; // 配列の一次要素数 [ ][ ][*] m_nRangeY = nRangeY; // 配列の二次要素数 [ ][*][ ] m_nRangeZ = nRangeZ; // 配列の三次要素数 [*][ ][ ] } int CHogeHoge::GetValue( int nAxisX, int nAxisY, int nAxisZ ) { return m_pBuffer[ nAxisX + nAxisY * m_nRangeX + nAxisZ * ( m_nRangeX * m_nRangeY ) ]; }

lachesis-r
質問者

お礼

試してみました。 2次元配列の場合、要素数が2^nとそうでない場合とでは30%程度パフォーマンスが落ちるようです。 乗算のコストとシフトのコストの差、よりはマシですが、命令スケジューリングが効いているのかもしれませんね。 なお、BCB6でMMXのSSE2を使って乗算+右シフトをasmで書いた場合とも比較しましたが、SSE2は50%程度までしか早くなりませんでした。 なかなかうまくいかないものです。

  • Soli
  • ベストアンサー率11% (7/61)
回答No.7

私のやり方が正しいかわかりませんが・・・ いつも多次元配列を考えるのが面倒なので、 int *P = new int[ x * y * z ]; // int P[z][y][x]のようにしたい場合 のように確保して、 int I = P[ X + Y*x + Z*(x*y) ]; // X,Y,Z は取り出したいデータが入ってる各軸の座標 のように取り出してます。 ご参考までに。 #処理が遅いかも…?

lachesis-r
質問者

お礼

ありがとうございます。 1次元配列に置き換えるということですね。 これだと要素数が2のn乗でない場合には遅くなるかもしれませんね。 (すみません。試していません) クラスにして要素へのアクセスを[]演算子でできるようにして・・・という感じでしょうか?

  • ency
  • ベストアンサー率39% (93/238)
回答No.6

解決方法というよりも、C の配列の話をします。 ここから、lachesis-r さんのやり方でうまくいかない理由を汲み取ってもらえれば良いのですけど。。。 No4 Tacosan さんの回答がきちんと動作するか、未確認ですが、動作するのであれば、配列を使った解決手段としてはほぼ唯一ではないでしょうか。 C の配列はコンパイラが先頭要素へのポインタに読み替えますが、これは再帰的には適用されません。 つまり、配列の配列の配列 (3次元配列) では、配列の配列の先頭要素へのポインタに読み替えられますが、さらにそこからポインタへの読み替えは起こりません。 つまり、以下のようになるわけです。 [例] ------------------------------------------------ // NEW int **mat = new int*[elem2][elem1] for ( i = 0; i < elem2; i++ ) { for ( j = 0; j < elem1; j++ ) { mat[i][j] = new int[elem3]; } } // DELETE for ( i = 0; i < elem2; i++ ) { for ( j = 0; j < elem1; j++ ) { delete [][] mat[i][j]; } } delete mat; ------------------------------------------------ ですので、すでに回答にある方法を参考にするほうが、有用だと思います。 # C++ 自体よく知らないものでして。。。 $ うまい解決方法は、詳しい方々におまかせします。。。 とりあえず参考程度に。。。

lachesis-r
質問者

補足

ありがとうございます。 いろんな方法があるものですね・・・ 少し整理したいのですが、仕事が忙しくなかなか空き時間がとれません。 delete[][] というのがミソでしょうか。恥ずかしながらはじめて見ました。

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.5

> vectorはコンパイル速度も実行速度も犠牲になりますし、アルゴリズムも範囲チェックも不要なので、出来れば避けたいと思います。 実際に計測していただければわかると思いますが、vectorを使っても実行速度はほとんど変わらないはずです。(ライブラリの実装にもよるところも多少ありますし、CINTのようなインタープリタであればやはり遅くなるでしょうが...) > これだと、添え字の異なる(型が同一の)3次元配列をひとつの関数で受けられません。 > 例えば、a[10][20][30]とb[60][70][10]のような関数を同一の関数に渡す事ができません。 > > 何か良い方法がありますか? template <int M, int N> void func(int (*p)[M][N]); のようにすれば、1つの関数で済みます。(実体は複数出来ます)

lachesis-r
質問者

お礼

なんとかテスト再開できました。 A,Bが動的2次元配列、AVがvecorによる2次元配列で、 for(int i=0;i<count;i++) for(int y=0;y<DIM;y++) for(int x=0;x<DIM;x++) A[y][x]= A[y][x] *B[DIM][DIM]/256; for(int i=0;i<count;i++) for(int y=0;y<DIM;y++) for(int x=0;x<DIM;x++) AV[y][x]= AV[y][x] *B[DIM][DIM]/256; これだと 前者: 805.275ms 後者: 1833.780ms でした。 (DIM:512、count:1000) Pen4 3.2G WinXPPro BCB6Pro ふと思ったのは、vectorはイテレータを使わないと早くならないのかしら?ということですが、そこまで手が回りませんでした^^; ※コンパイル速度は遅いと感じるほどには変わりませんでした。 ※vectorは要素チェックは行いませんね。不適切な発言でした。

lachesis-r
質問者

補足

ありがとうございます。 今、仕事がバタバタしておりまして、手が空き次第、試してみます。 実行速度については、以前の予備テストのおぼろげな記憶でして、不適切な発言だったかもしれません。 これについても、再度確認します。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.4

// 確保 int ***mat; mat = new int **[a]; mat[0] = new int *[a*b]; mat[0][0] = new int [a*b*c]; for (int i = 0; i < a; i++) { mat[i] = mat[0] + i*b; mat[i][0] = mat[0][0] + i*b*c; for (int j = 0; j < b; j++) { mat[i][j] = mat[i][0] + j*c; } } // 開放 delete [] mat[0][0]; delete [] mat[0]; delete [] mat; くらいかなぁ? 実行してないので動作の保証はできませんが....

lachesis-r
質問者

お礼

ありがとうございます。 動きました。 このようにする方法もあるんですね。 勉強になります。

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.3

学習目的であれば別ですが、実用を考えると次のようにした方がよいと思います。 要素数が固定でよい場合... int (*p)[M][N] = new int[num][M][N]; 各次の要素数が可変の場合... std::vector<std::vector<std::vector<int> > > a; なるべくstd::vectorを使うことをお薦めします。 (解放の手間も省けることですし...)

lachesis-r
質問者

お礼

>a[10][20][30]とb[60][70][10]のような関数を a[10][20][30]とb[60][70][10]のような「配列」を です。申し訳ありません。

lachesis-r
質問者

補足

ありがとうございます。 vectorはコンパイル速度も実行速度も犠牲になりますし、アルゴリズムも範囲チェックも不要なので、出来れば避けたいと思います。 int (*p)[M][N] = new int[num][M][N]; の使い方も試してみました。 これだと、添え字の異なる(型が同一の)3次元配列をひとつの関数で受けられません。 例えば、a[10][20][30]とb[60][70][10]のような関数を同一の関数に渡す事ができません。 何か良い方法がありますか? (未だに***p でのdeteteがうまくいかない・・・)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

2次元でできれば 3次元だってすぐだと思うけど.... * が 1個増えるだけ (new/delete も増えるけど) でしょ?

  • kenipi
  • ベストアンサー率29% (44/150)
回答No.1

こんなところをご覧になってはいかがでしょうか。

参考URL:
http://sometime.minidns.net/~ccgi/pointer_array.html

関連するQ&A

  • 多次元配列のメモリ解放

    多次元配列のメモリ解放についてです。 以下のような方法で多次元配列を確保した場合に、 --- char** ppMain; ppMain = new char*[3]; for (int i = 0; i < 3; i++){ ppMain[i] = new char[20]; } --- メモリ解放する場合、 --- for (int i = 0; i < 3; i++){ delete [] ppMain[i]; ppMain[i] = NULL; } delete [] ppMain; ppMain = NULL; --- で良いでしょうか? おそらく、new/deleteの回数が同じであれば問題ないと思うのですが。 少し混乱してしまって、 delete [] ppMain[i]; によって new char*[3]で確保したところも解放されており delete [] ppMain; が必要なく危険な領域まで解放しようとしているということはないでしょうか? ご専門、お詳しいかたコメント宜しくお願いします。

  • 動的配列を宣言するためにnew演算子とdelete演算子を用いる方法が

    動的配列を宣言するためにnew演算子とdelete演算子を用いる方法があります。 2次元配列は以下のように宣言して作れるのは分かったのですが、 (実際に使って動かしてみました。) int **pp; pp = new int*[ROW]; // 行を作る for(int i = 0; i < ROW; i++) // 列を作る pp[i] = new int[COL]; http://www.asahi-net.or.jp/~uc3k-ymd/Lesson/Section02/section02_07.html(引用) 3次元配列をどのように作ったらいいか分かりません。 教えてください。宜しくお願いします。

  • 三次元配列の動的メモリの確保?

    const int SLICE=2; const int SIZE=256; signed short int *matrix=new signed short int[SLICE][SIZE][SIZE]; for(int i=0; i<SLICE; i++){ for(int j=0; j<SIZE; j++){ for(int k=0; k<SIZE; k++){ fin.read((char*) &matrix[SLICE][SIZE][SIZE],sizeof(signed short int)); } } } delete[] data; 三次元データを読み込むために、三次元配列を使って読み込もうとしたのですが、上手く読み込めません。 三行目の所で、error C2440: '初期化中' : 'short (*)[256][256]' から 'short *' に変換できません。 七行目の所で、error C2109: 配列または、ポインタでない変数に添字が使われました。 というエラーがでます。動的メモリの確保の仕方がまずいのでしょうか? どなたか教えて頂けますでしょうか?よろしくお願いします。

  • 多次元配列について

    int[]a = {1,2,3} int[]a = new int[3];//は普通の配列 int[]a = {{1,2,3},{4,5,6}}; int[][]a = new int[3][3]は //2次元配列 int[]a = {{1,2,3}{4,5,6}{7,8,9}};int[][][] = new int[3][3][3]; //は3次元配列 int[][][][]a = new int[3][3][3][3]; //は4次元配列になると思いますがこっちの方はコンパイルエラーが出ないのに int[][][][]a = {{1,2,3},{4,5,6},{7,8,9},{10,11,12}}; //はコンパイルエラーが出ます どうしてですか?

  • ポインタ配列の開放について

    いつもお世話になっております。 C++言語初心者です。 ポインタ配列の開放(delete)について質問です。 ※includeは省略します。 int main(){   int *a[10];   for(int i=0;i<10;i++){     a[i]=new int[5];   }   delete[] *a;   return 0; } ポインタ配列を開放する場合、 上記のような書き方で正しいのでしょうか? ただ、上記のような記述方法が間違っている場合、 for(int i=0;i<10;i++){   delete[] a[i]; } とやるのはスマートでない気がするので、 もし他に方法がありましたらお願いします。

  • 「動的確保した2次元配列のメモリ解放」を関数化したい

    質問タイトルの通りですが、 「動的確保した2次元配列のメモリ解放」をC言語で関数化したいと思っています。しかし、関数の引数には動的確保した配列の先頭アドレスのみ渡す形にしたいです。そのような場合の関数化は可能ですか? どうもうまくいかず、困っています。 以下、具体的に、サンプルソースを記述します。 わかる方、よろしくお願いします。 //====================================================// #include<stdio.h> unsigned char** AllocByteArray2d(int column, int row); void FreeByteArray2d(unsigned char** box); int main(voidls){ unsigned char array**; array = AllocByteArray2d(2, 3); FreeByteArray2d(array); return 0; } unsigned char** AllocByteArray2d(int column, int row){ unsigned char* box; box = (unsigned char**)malloc( sizeof(unsigned char*)*column ) int i; for(i=0; i<column; i++){ box[i] = (unsigned char*)calloc( row, sizeof(unsigned char)); if(box[i] == NULL) exit(EXIT_FAILURE); } return box; } //引数では配列の先頭アドレスだけ渡す形にしたい void FreeByteArray2d(unsigned char** box){ //ここをどう書いたらいいかわからない }

  • 3次元配列を1次元配列に

    例えば2次元配列だと, array[row*i+j] = a[n]; といったように1次元に直すことができますよね? 同様に3次元配列を1次元配列にしたい場合には arrayの中はどのような式をつかえばいいのでしょうか。 よろしくお願いします。

  • 動的に確保した配列のファイルへの書き出し

    動的に確保した配列を、ファイルに書きだそうとしています。 int i, arraysize = 10; int **array; array = new int*[ arraysize ]; for (i = 0 ; i < arraysize ; i++) array[ i ] = new int [ arraysize ]; ofstream out("filename", ios::out | ios::binary); out.write((char *) array, sizeof( array )); これでは array のポインタが書き込まれるだけ、なのかな ? ということで、配列を書き込む目的を果たせておりません。このように動的に確保した多次元配列 (要素数は既知) をファイルに書き込むためにはどうすればよろしいでしょうか。

  • 多次元配列の受渡しでの警告

    多次元配列を関数に受け渡す際に、 警告: passing argument 1 of ‘mat_add’ from incompatible pointer type 警告: passing argument 2 of ‘mat_add’ from incompatible pointer type のような警告が出てきてしまいます。 プログラムは、明解C言語(柴田望洋著)で紹介されているサンプルプログラムなのですが(下に載せておきます)、どうしてこのような警告がでるのか分からず困っています。 多次元配列の渡し方になにか問題があるのでしょうか? #include <stdio.h> void mat_add(const int ma[][3], const int mb[][3], int mc[][3]) { int i,j; for(i=0; i<2; i++) for(j=0; j<3; j++) mc[i][j] = ma[i][j] + mb[i][j]; } int main(void) { int i,j; int ma[2][3] = {{9,2,-3},{4,5,1}}; int mb[2][3] = {{9,2,-3},{4,5,1}}; int mc[2][3] = {0}; mat_add(ma, mb, mc); for(i=0; i<2; i++){ for(j=0; j<3; j++) printf("%3d",mc[i][j]); putchar('\n'); } return(0); }

  • 二次元配列に関する質問です。

    一次元配列はわかるのですが二次元配列になると、わからない時があります。 <ソース> #include<stdio.h> int main() { int dat[2][5]={1,2,3,4,5,6,7,8,9,10}; int i; int j; for(i=0;i<2;i++){ for(j=0;j<5;j++){ printf("dat[%d][%d] %d\n",i,j,dat[i][j]); } } return 0; } このプログラムでわからないのは、 int dat[2][5]={1,2,3,4,5,6,7,8,9,10};の部分です。 普段は、int dat[2][5]={{1,2,3,4,5}, {6,7,8,9,10}, }; という使い方をしているのですが・・・。 どうちがうのでしょうか?

専門家に質問してみよう