• ベストアンサー

メモリ開放の質問ですが

#include<iostream> using namespace std; void main(void) { int i; char *s,*t; s=new char[9];t=s; for(i=0;i<9;i++)*(s+i)=i; for(i=0;i<9;i++)cout<<(int)s[i]<<endl; delete s; delete t; //error } において2回目のdelete tでエラーになりますが #include<iostream> using namespace std; void main(void) { int i; char *s,*t; s=new char[9];t=s+2; for(i=0;i<9;i++)*(s+i)=i; for(i=0;i<9;i++)cout<<(int)s[i]<<endl; delete t; delete s; } はエラーになりません (s=t+2をs=t+1に変更するとエラーになる) 実際にdeleteは何を行いエラーを引き起こすのでしょうか? エラーになるメカニズムを教えてください

  • keyguy
  • お礼率68% (895/1314)

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

  • ベストアンサー
  • terra5
  • ベストアンサー率34% (574/1662)
回答No.4

文法上できません。 また、mallocを使っても, s=(char *)malloc(9);t=s+2; free(t);free(s); のような使い方はできません。 エラーが発生しないことがありますが、たまたまです。 異常な動作をしたが、見かけ上は正常に動作したように見えるだけです。 エラーが起きなかったのは正しいという保証にはなりません。 実際,malloc()やfree()が何をしているかがわかると、 こういうことをすると異常な動作をおこすことがわかると思います。 例えば,char buff[BUFSIZE];というテーブルから、 メモリを確保,開放するmalloc(),free(),realloc()を作ることを考えて見て下さい。 途中でfreeできるようにもできないことは無いと思いますが、オーバーヘッドが大きくなりすぎて実用的には使えないでしょう。 また、どこかでmalloc(),free(),reallo()のソースを読んでみてもわかるでしょう。

その他の回答 (5)

回答No.6

単にメモリー解放をしたいだけならば既存のソフトを使用してはいかが?僕は「びーめむ」を使っている。 その他に[CoolMemory 1.0],[MemClean 1.03],[MemSouji 1.00]全てフリーウェアです。

参考URL:
http://www.vecter.co.jp/soft/hardwawe/se136974.html
keyguy
質問者

お礼

まとめてありがとうございます 方針を変えてオーソドックスに new-deleteの1対1対応の開放をしようと思います 若干管理が大変になりますがそれほどでもないのでそうしようと思います

  • mitoneko
  • ベストアンサー率58% (469/798)
回答No.5

 #3に補足しておきます。  ソースから見た答えは、まぁ、#2・#3のとおりなわけですが・・・  ここからは、アブケーションとOSに依存する話です。  あなたは、ほんとうに、連続したメモリーアドレス空間を確保した上で、任意のメモリーアドレスを解放する必要があるのでしょうか?BCC++でコンパイルしていると言うことは、多分、OSは、windows95以降だと思いますが、windowsのアプリでそんな器用なメモリーアドレス管理が必要なことは・・・無いような気がします。もしこれが本当に必要なら、OSのメモリー管理APIを直接操作してメモリーを操作するべきです。  あるオブジェクト(まぁ、intでもcharでも任意のクラスでもかまいませんが)がいくつかあって、これの個数も判らず、確保と解放の順番も不明と言うのであれば、配列でメモリーを確保するのが、多分間違っています。リンクリスト(双方向か片方向かは、アプリケーション次第です。)で管理するものでしょう。  あるバッファ領域があって、その必要なバッファ領域の大きさが変動するのであれば、必要と思われる最大の領域を確保して、その領域をそのまま利用するか、vectorクラスの利用を検討するのが簡単でしょう。(タイムリーに領域を縮小しOSに返却する必要は、多分ないでしょう。ほんとに必要ですか?)  何にせよ、配列の一部を開放するという発想から離れた方がよろしいかと・・・

  • mitoneko
  • ベストアンサー率58% (469/798)
回答No.3

 まず、#1の補足に書かれたソースも、致命的なエラーです。その後に、何もやっていない単なるテストプログラムだから動いているように見えるだけです。(delete2行の後にsの周辺に何か確保していろいろとやってみると良いです。まぁ、まともに動くことはないでしょう。) >1つの大きなメモリを確保してそれを少しづつ部分的に開放したいのでこのような開放の仕方をできるかどうかテストしているのです  new deleteでは、このようなメモリーの確保・解放はできません。ついでにいうと、malloc・freeの組み合わせでも不可です。(場合によってはreallocが使えることがありますが、おそらく今回の場合は適用できません。)  そもそも、このように、少しづつ部分的に解放するなどという用途が必要なら、自分が必要とする最小のブロックでメモリー確保を繰り返し、そのメモリーのリストを自分で管理して、不要になったブロックから、一つづつ解放していくものでしょう。(それなら、new deleteでも、malloc・freeでも可能です。)  実際に、どんな用途で、メモリーを解放するのかよくわかりませんが、少なくとも、配列に確保されたメモリーの一部を開放するような方法は、CにもC++にも無いと思います。(STLのvectorクラスならまだ目がありますが・・・ただ、これは、メモリー確保・解放に関しては、完全にライブラリーに任せるのが基本です。そんなにきめ細かくメモリーの確保や解放を気にするためのものでもないですし、コントロールもできません。もっとも、大概の用途においてそれでも全然差し支えないわけですが。)

  • taka_tetsu
  • ベストアンサー率65% (1020/1553)
回答No.2

>1つの大きなメモリを確保してそれを少しづつ部分的に開放したいのでこのような開放の仕方をできるかどうかテストしているのです そんな解放はできません。 そもそも、動的に領域のサイズを変化させたいのであれば、newではなくmalloc、reallocでしょう。 newはクラスのインスタンスを生成し、コンストラクタを呼ぶものです。deleteは、newで作成したインスタンスを破棄し、デストラクタを呼んで領域を解放するものです。 バッファの確保等の用途に使うものではありません。(使えますが)

  • mitoneko
  • ベストアンサー率58% (469/798)
回答No.1

 致命的な文法違反があります。  そのため、エラーになるメカニズムもなにも・・・と言ったクラスです。 s=new char[9]; に対応するdelete文は、 delete [] s;  です。  配列のメモリー領域を、単なるdeleteで解放してはいけません。  これは、上のプログラムも下のプログラムも両方とも同じエラーを起こしています。そして、このミスが、さらに悪いことに、下のプログラムでエラーが「発生しない」原因です。(そう。エラーが発生「しない」のがすでにおかしいのです。)  また、newで確保したメモリーは、必ず1回「だけ」deleteしなくてはなりません。  ポインターtは、ポインターsの単なるコピーですから、sに対してdeleteした後に、tのdeleteをするということは、同じメモリーに対して二度deleteをしていることになります。(ポインターのコピーというのは、そういうことです。)これは絶対にやってはいけません。  下のプログラムで運悪くエラーが発生しなかった原因は、ほとんど調査の意味がありません。その理由は、配列をnewで確保した後、その配列をdelete []ではなくdeleteで解放した場合のプログラムの挙動は「不定」だからです。(コンパイラは、これを考慮する必要が無く、万が一このような事態があった場合は、なにをやらかしてもかまわないという意味です。辛辣な書籍に言わせれば、この場合は、コンパイラーはHDDのフォーマットを始めるコードを生成してもかわまないとまで書いた本もあります。)  ここで、プログラムが終了しているから良いようなものの、sの付近のメモリーは、多分カオス状態ですから、これ以降は、もう何が起こっても不思議ではありません。  最後に、もう一度結論を。  deleteは、newで確保されたメモリーを解放します。  delete []は、newで確保された配列メモリーを解放します。 s = new char[9];  と確保した配列領域は、必ず、「一度だけ」 delete [] s;  で解放しましょう。  ポインターをコピーした場合は、同じ領域を二度deleteしないように注意しましょう。コピー元とコピー先の両方でdeleteしてはいけません。

keyguy
質問者

補足

ありがとうございます #include<iostream> using namespace std; void main(void) {  int i;  char *s,*t;  s=new char[9];t=s+2;  for(i=0;i<9;i++)s[i]=i;  for(i=0;i<9;i++)cout<<(int)s[i]<<endl;  delete []t;  delete []s; } もエラーが発生しません コンパイラはBorland C++5.51ですがコンパイラの問題ですか? 1つの大きなメモリを確保してそれを少しづつ部分的に開放したいのでこのような開放の仕方をできるかどうかテストしているのです

関連するQ&A

  • 2つのcppファイルから

    1つのexeファイルを作る必要性が出てきました。 (現在質問中の質問に関連しています。) 例えば file.cpp: #include<iostream> using namespace std; void Output(int x) { cout<<x<<endl; } void main(void) { for(int i=0;i<9;Output(i++)); } をコンパイルリンクすれば済むことを file1.cpp: #include<iostream> using namespace std; void Output(int x) { cout<<x<<endl; } file2.cpp: void main(void) { for(int i=0;i<9;Output(i++)); } という風に分かれているファイルをコンパイルリンクしなければならないのです。 cpp -e file.exe -c file1.cpp -c file2.cpp ではエラーになります。 どうすれば良いのでしょうか?

  • int *a = new int; エラー

    下記をコンパイルすると、'int' 型は 'int *' 型に変換できない(関数 main() ) とエラーが出てしまいました。 何がおかしいのでしょうか ? 初歩的な間違いをしていると思いますが、アドバイスお願いします。 #include<iostream> #include<stdlib> #include<ctime> using namespace std; void main(void) { int *a = new int; a=rand(); cout<<a<<endl; delete a; }

  • char型変数のアドレスを coutで表示するには

    #include <iostream> using namespace std; int main() { bool b; int i; short s; long l; float f; double d; char c; //上で宣言した変数のアドレスを表示 cout << "bool &b " << &b << endl; cout << "int &i " << &i << endl; cout << "short &s " << &s << endl; cout << "long &l " << &l << endl; cout << "float &f " << &f << endl; cout << "double &d " << &d << endl; cout << "char &c " << &c << endl; //「char &c 」とのみ表示される cout << '\n'; //char型のみ printf で再表示 printf("char &c %p\n", &c); //「char &c ********」と表示される return 0; } 上のプログラムを実行すると cout << "char &c " << &c << endl; のところだけ、アドレスが表示されません。 printfを使えば、char型の変数のアドレスも表示されるのですが…。 coutを使ってchar型のアドレスを表示させるにはどうすればいいのでしょうか。 よろしくお願いします。

  • 配列の要素

    #include <iostream> using namespace std; int main() { int n[10] ={1,2,3,4,5,6,7,8,9,10}; int i; for(i = 0; i < 10; i++){ cout << "a[" << i << "] = " << n[i] << endl; } return 0; } ここまでは完成することはできたのですが この要素の並びをシャッフルしてランダムな順に並び変える方法がわかりません。

  • プログラムの動作

    10文字をスキップするプログラムなのですが、どのようにスキップしているのかわからないので教えてください。 下にソースコードを書きます。 #include <iostream> using namespace std; //10文字をスキップする istream &skipchar(istream &stream) { int i; char c; for(i=0; i<10; i++)stream >> c; return stream; } int main() { char str[80]; cout << "いくつかの文字を入力する:"; cin >> skipchar >> str; cout << str << endl; return 0; } よろしくおねがいします。

  • アドバイスお願いします

    二つの顔文字を横移動しながら交互に変換したいんですけど 横移動しながら変換できるようにはなったんですけど 一番最初に顔文字が残ってしまい不自然なんですけどどうすれば消えるでしょか?アドバイスお願いします #include<iostream> using namespace std; int main() { int i,k,t; char str[] = "(~~)"; char str2[] = "(<>)"; for(i = 1; i < 10; i++){ cout << " "; for(k = 0; k < i; k++){ cout << " "; } for( t = 0; t < 2000; t++ ) if( i % 2 == 0 ){ cout << str; cout << '\r'; }else{ cout << str2; cout << '\r'; } } cout << "\n"; return 0; }

  • C++

    今、下のようなプログラムを作っています #include <iostream> #include <iostream> using namespace std; int i=0, c=0, n; char str[10]; class X16karax10{ //16進から10進ヘ public: void keisan(); }; void X16karax10::keisan(void){ cout<<"16進を入力して下さい"<<endl; cout<<"英数字は大文字で入力してください(F→○ f→×)" <<endl; scanf("%s",str); while(str[i] != '\0'){ n = n * 0x10; c = str[i++]; if((c >= '0') && (c <= '9')){ n += c - '0'; } else if((c >= 'A') && (c <= 'F')){ n += c - 'A' + 10; } } cout<<("%d\n",n)<<"です\n"<<endl; } int main(){ for(i=0; ; i++){ X16karax10 p; p.keisan(); } } 16進を十進に変えるものなのですがreturn 0を使うと「X16karax10::keisan()' は値を返せない」と、でてしまうのですがどうしたらよいでしょうか?

  • コマンドライン引数

    下記のリストの結果は自分の期待では「真」なのですが 「偽」となります。 なぜなのかわけが分からなくなって質問させて頂きまし た。 (なお後尾の2行はチェックするために入れただけです。) どなたかご教示願えませんでしょうか。 よろしくお願いします。 リスト #include <iostream> using namespace std; int main(int argc, char *argv[]) { if( argv[1] == "yes") { cout << "真です。" << endl; } else { cout << "偽です。" << endl; } cout << " argv[1] は " << argv[1] << " です。" << endl; cout << " argv[2] は " << argv[2] << " です。" << endl; }

  • 文字列検索について

    下記のようなC++プログラムにおいて、Good Morning! の「r」以後が、 検索にひっかからないで、-1を返します。 何故なのか、どうぞよろしくお願いします。 -------------------------------------------------------- #include <iostream> #include <cstring> using namespace std; int flag = 0; // 該当文字があったかどうかのフラグ int strch_idx(const char* s, char c){ int temp; cout << strlen(s) << endl; for(int i=0; i<(signed)strlen(s); i++){ if(*s == c){ temp=i+1; // 配列のインデックスは0オリジンだが、インデックスは1からだから1+する flag = 1; break; } s++; } if(flag == 1) return temp; else return -1; } int main(){ const char* s = "Good Morning!"; int idx = strch_idx(s, 'i'); cout << "検索文字のインデックス:" << idx << endl; return 0; }

  • char型配列について

    基本的なことですが、 char str[5]="Hello"; --> str[0]='H' str[1]='e' str[2]='l' str[3]='l' str[4]='o' str[5]='\0' では、ないのでしょうか? エラーが出ます。 //error C2117: 'str' : 指定された配列には、初期化子が多すぎます。 char str[6]="Hello"; では、コンパイルできます。 ---------------------------------- また、 #include<iostream> using namespace std; int main() { char str[6]="Hello"; cout << str << endl; for(int i=0;i<7;i++) { cout << "i=" << str[i]; if(str[i]=='\0'){cout << " NULL" << endl;} else{cout << endl;} } getchar();return 0; } ----------------------------------------------- とすると、 Hello i=H i=e i=l i=l i=o i= NULL <--ここで、NULLなら、 i=フ <--このぶんは、いらないと思うのですが、、、 となります。 str[6] i=6 は、何を意味するのでしょうか? Visual C++ NET を使用しています。 よろしくお願いします。

専門家に質問してみよう