• 締切済み

多次元配列のメモリ解放

多次元配列のメモリ解放についてです。 以下のような方法で多次元配列を確保した場合に、 --- 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; が必要なく危険な領域まで解放しようとしているということはないでしょうか? ご専門、お詳しいかたコメント宜しくお願いします。

みんなの回答

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

> それを考えると以下の方法であっても、 > 例外が送出される場合があるのではないでしょうか? > char (*pMain)[20] = new char[3][20]; 例外が送出される可能性はあります。 しかし、上の場合では、メモリの割り付けに成功するか、例外が送出されてメモリがまったく割り付けられないかのどちらかですので、中途半端な割付けが行われてメモリリークすることがありません。

ring_rollo
質問者

補足

ご回答ありがとうございます。 "中途半端な割付け"の意味をいろいろ調べていて、 連絡遅れてしまいました。 調べても全く違いが分からなかったのですが、 (1)の場合の中途半端な割付けというところをもし良ければ 少しコメントいただけないでしょうか? ■(1) for (int i = 0; i < 3; i++){ ppMain[i] = new char[20]; } ■(2) char (*pMain)[20] = new char[3][20]; jactaさんの回答では、 (2)の場合は、割付けに失敗すればNULLが返って来るので問題ない。 (1)の場合には割付けに失敗してもNULLが返ってこない(適当なアドレスが返ってきてしまうので例外チェックも必須)ということだと思いますが、 その違いは何でしょうか? (1)と(2)の違いというと1次元配列、2次元配列の違いしか私には分からないのですが、 その違いでメモリ割り当て方法が大きく変わってくるのでしょうか?

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

> (1)の場所で例外が送出? > というのはどういうことでしょうか? メモリの割付けに失敗した場合、newはstd::bad_alloc例外を送出します。 その場合、既に割付け済みのメモリブロックは解放の機会を失いますので、メモリリークにつながります。 これの回避は面倒ですが... char** ppMain; ppMain = new char*[3]; std::fill_n(ppMain, 3, 0); try{  for (int i = 0; i < 3; i++){   ppMain[i] = new char[20];  } } catch (std::bad_alloc&){  for (int i = 0; i < 3; i++){   delete[] ppMain[i];  }  delete[] ppMain; } のようにする必要があります。 これが面倒であれば、 char (*pMain)[20] = new char[3][20]; とすれば、簡単になります。 ただし、ポインタ配列ではないのでサイズは固定です。 更に簡単にするには、 std::vector<std::vector<char> > vMain(3, std::vector<char>(20, '\0')); とでもすればよいでしょう。 これなら可変サイズにも対応できますし、明示的な解放が不要になります。

ring_rollo
質問者

お礼

ご回答ありがとうございます。 new / deleteというものをよく知らず、 用語をググりながら内容理解しました。 メモリ不足なり、何らかの理由でメモリアロケートに失敗し、 例外が起こる可能性があり、チェックが必要ということですね。 それを考えると以下の方法であっても、 例外が送出される場合があるのではないでしょうか? char (*pMain)[20] = new char[3][20]; 着目点が間違っていますでしょうか?

  • iiroty
  • ベストアンサー率25% (3/12)
回答No.3

結論としては、お示しのコードで問題ありません。 以下、ちょっと細かく解説します。 まず、「new char*[3];」は「多次元配列」ではなく「ポインタの配列」を確保していることに気をつけてください。 両者はメモリ上のデータ配置が全く異なります。 前者はある空間に char が3×20並んでいることになりますが、後者はアドレス値が三つ並んでいるにすぎません。 そして「new char[20];」の繰り返しで得られるポインタ群は連続している空間を指しているとは限らず、ましてや「new char*[3];」とは全く関係のない場所である可能性もありえます。 そう考えると、「delete [] ppMain[i];」の時点では各「new char[20];」で確保した部分を開放しており、「delete [] ppMain;」では「new char*[3];」で確保したところを開放していることが理解しやすいと思います。

ring_rollo
質問者

お礼

ご回答ありがとうございます。 多次元配列だと、こんな感じのことですね。 char hairetu[][][]; 失礼しました。 ポインタの配列のみを解放しているという感じが不安でした。

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

解放側は問題ありませんが、割付け側に問題があります。 char** ppMain; ppMain = new char*[3]; for (int i = 0; i < 3; i++){ ppMain[i] = new char[20]; // (1) } (1)の場所で例外が送出されると、確実にメモリリークが発生します。

ring_rollo
質問者

お礼

ご回答ありがとうございます。 (1)の場所で例外が送出? というのはどういうことでしょうか? よろしくおねがいします。

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

うい. それで OK.

ring_rollo
質問者

お礼

ご回答ありがとうございます。

関連するQ&A

  • 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時間半は試行錯誤はしているのですが、たどり着きそうにありません。 どなたかご教授願います。

  • 「動的確保した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){ //ここをどう書いたらいいかわからない }

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

    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: 配列または、ポインタでない変数に添字が使われました。 というエラーがでます。動的メモリの確保の仕方がまずいのでしょうか? どなたか教えて頂けますでしょうか?よろしくお願いします。

  • 多次元配列の動的メモリ

    1次の配列aの動的メモリは例えば 「double *a;  int num = 3; //配列の数  a = new double [3];」 などとすれば確保できることは分かるのですが、 二次元配列など、多次元配列の動的メモリは どのようにすれば確保できるのですか?

  • 動的メモリ 解放がうまくいかない

    よろしくお願いします。 一ファイル20万行程度のCSV形式のテキストファイルが、50個ほどあります。 これを一行づつ読み込んで、strtok( ,",")でデータを取得しようと思っています。 ファイルの行数はまちまちなので、新しいファイルを読み込むときに そのファイルの行数を調べて(ここでは count 行あります) callocをつかって、メモリを確保しました。 btxt=(char **)calloc(count,sizeof(char *));/*動的メモリ確保*/ for(i=0;i<count;i++) btxt[i]=(char *)calloc(120,sizeof(char)); /*一行120文字まで*/ if(btxt==NULL){printf("btxt 確保エラー\n"); exit(0);} 上記btxt配列にすべての行数を読み込んで、strtok()処理をした後 for(v=0;v<120;v++){ if(btxt[v]){ free(btxt[v]); btxt[v]=NULL; }  } free(btxt);  btxt=NULL; で解放してから、次のファイルに移ります。 問題は、ループするごとにメモリ容量がどんどん減ってきて、30ループもさせると メモリ不足でエラーが出ることです。 ブレークポイントを使って調べてみたのですが ループ一回目 calloc前 707.7 MB: calloc後 748.6MB 解放(したつもり)後 747.9 MB ループ二回目 calloc前 750.6 MB: calloc後 794.6MB 解放(したつもり)後 793.8 MB ・・・・・・・・・・・・・・・・・・・ ループ四回終了時には868.3MBにもなって、初めより160MBも使ってしまいます。 free()が効いてないと思うのですが、どこがおかしいのか教えてくださいませ。

  • 配列のメモリの確保

    先日メモリについてご質問させていただいたものですが、 今ファイルから読み込んだ文字列を配列に格納する作業を行なっています。 今は char buf[1000]; FILE fp; if((fp=fopen("○○.txt","r")) ==NULL){ printf("ファイルが開けません"); } while(fgets(buf,1000,fp) != NULL){ としてファイルを一行ずつ読み込んでその後単語ごとに配列に組み込みます このときファイルの文字列を格納する配列はbuf[1000]ですが このメモリでは足りないかもしれませんし多すぎるかもしれません。 足りない場合はエラーになるし多すぎる場合はメモリの無駄ですよね。 このような場合はメモリを取り直すべきなのでしょうか?その場合 どのような方法がありますか?調べてもint型の領域確保とかそういうのはあるんですがファイルから読み込んだ文字列の領域確保とかは見つからなかったので教えて下さい。

  • 2次元配列の動的確保

    ある画像を読み込むため、その画像を格納できる幅、高さを持った配列を動的に確保しようと考えています。 幅をxsize、高さをysizeで次のように記述しました。 unsigned char **src; int i; src = (unsigned char**)malloc(sizeof(unsigned char*) * ysize); src[0] = (unsigned char*)malloc(sizeof(unsigned char) * xsize * ysize); for(i=1; i<ysize; i++) src[i] = (src[0] + i * xsize); わざわざポインタのポインタを使用したのは、動的に確保した配列を2次元的なアクセスをしたかったためです。 画像の読み込み時は fread(src[0], sizeof(unsigned char), xsize * ysize, fp); としています。 上に記述したソースは問題なく動作しました。 しかし、上の場合だと全ての配列を連続して確保することができません。つまりsrcでmallocを一回、src[0]でmallocを一回使っているため、ポインタの配列の直後に配列を確保する保障がありません。そこでいっぺんに確保することを考えました。 unsigned char **src; int i; src = (unsigned char **)malloc(sizeof(unsigned char *) * ysize + sizeof(unsigned char) * xsize * ysize); for(i=0; i<ysize; i++) src[i] = (unsigned char *)(src + sizeof(unsigned char *) * ysize + i * xsize); このように組み上げ、読み込み時は上のfreadと同様に記述したところエラーが出てしまいました。 やはり一行目のmallocで無理やりsizeof(unsigned char *) * ysize + sizeof(unsigned char) * xsize * ysize分確保するのは失敗だったのでしょうか?

  • 多次元配列の new

    多次元配列を new すると、どのような型のサイズの領域の配列が確保されるんでしょうか?たとえば、  int (*a)[2] = new int[3][2]; とすると、  1. 長さ2のintの配列へのポインタ型の長さ3の配列の領域が確保される のか、  2. int[3][2] すなわち、int が 6 つ分の領域が確保される のか。 今まで、「そりゃあ 2 の方だろう」と信じ込んであまり考えずにいたんですが、「コードの型形式からすると 1 の方の解釈でもいいよなぁ」と、ふと思ったものですから、質問させていただきました。 わたしの環境で調べてみると(配列用のハウスキーピング的な余分の領域とか、パディングなどは無視して)、確かに 1 の方なんですか、これで標準準拠なんでしょうかね?^^; XP Home Edition Ver.2002 SP2 cygwin v.1.0.2-1 GNU g++ v.4.1.1 ===== #include <iostream> #include <new> #include <cstdlib> struct A { char a; void *operator new(std::size_t s) { void *p = std::malloc(s); std::cout << "A::new(): " << p << '\t' << s << '\n'; return p; } void operator delete(void *p, std::size_t s) { std::cout << "A::delete(): " << p << '\t' << s << '\n'; if (p) std::free(p); } void *operator new[](std::size_t s) { void *p = std::malloc(s); std::cout << "A::new[](): " << p << '\t' << s << '\n'; return p; } void operator delete[](void *p, std::size_t s) { std::cout << "A::delete[](): " << p << '\t' << s << '\n'; if (p) std::free(p); } }; int main() { std::cout << sizeof(char) << '\t' << sizeof(A) << '\t' << sizeof(A(*)[8]) << '\n'; A *a = new A; std::cout << a << '\n'; A *aa = new A[8]; std::cout << aa << '\n'; A (*aaa)[8] = new A[8][8]; std::cout << aaa << '\n'; A (*aaaa)[8][8] = new A[8][8][8]; std::cout << aaaa << '\n'; delete[] aaaa; delete[] aaa; delete[] aa; delete a; } ===== % ./a.exe 1 1 4 A::new(): 0x870668 1 0x870668 A::new[](): 0x870678 12 0x87067c A::new[](): 0x870688 68 0x87068c A::new[](): 0x8706d0 516 0x8706d4 A::delete[](): 0x8706d0 516 A::delete[](): 0x870688 68 A::delete[](): 0x870678 12 A::delete(): 0x870668 1

  • 多次元配列について

    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}}; //はコンパイルエラーが出ます どうしてですか?

  • アドレス格納のための二次元配列のメモリ動的確保

    アドレス格納のための二次元配列のメモリ動的確保 二次元配列のためにメモリを動的確保しなければならないのですが、 その配列に格納したいものが 「DATA型のポインタ」です。(DATA型はtypedefした構造体です。) プログラム実行中にmallocで確保した、数あるDATA型の構造体の、その先頭アドレスを リストアップするための配列です。 この場合、どのような形でmallocすればよいのでしょうか? 教えていただけるとありがたいです。よろしくお願いいたします。 -- たとえば m×n のint型の配列は、 ◆ int *i; ◆ i = (int *)malloc( m * n * sizeof(int) ); となりますよね。 この要領がでやるのが一般的にわかりやすいものだとするならその方法でやりたい (後発の人が自分のソースコードを読む可能性があるため)のです。 -- 同様にm×nの「DATA型のポインタを格納するための二次元配列」を動的確保したい場合、 ◆ DATA *d; ◆ d = (DATA *)malloc( m * n * sizeof(DATA) ); この文にどのように付け加えたら良いのでしょう? もうあと一歩な気がするのですが(笑)。しかし参考書等で勉強しましたがわかりませんでした・・・。 わかる方、どなたかよろしくお願いいたしますm(_ _)m あとこれだけ通ればコンパイルが通るんです!!!!! たぶん(笑)

専門家に質問してみよう