• ベストアンサー

エラー処理,メモリ開放,exit

こんばんは。現在、次のような C のプログラムを作っています。 ・ 動的にメモリを確保した配列や構造体が複数ある。 ・ しばしば,関数呼び出しのネストが複数(2~4) になる。 ・ ”エラーチェック => エラーなら終了” という動作が多い。 このようなプログラムで,"エラー => プログラム終了" の際,動的に確保したメモリを,関数呼び出しのネストを(return文 で)遡って,全て開放すべきでしょうか。 このようにすると,あまりにプログラムが煩雑になるので,今は "エラー => exit(-1) で直ちに終了" としています。 しかし改めて C の教科書を見ると,exit の時,オープンされているファイルやストリームについては,捨てられ,あるいは閉じられる,と書いてありますが,メモリについては何も書かれていないので,どうするべきか少し悩んでいます。 みなさんはこんな場合,どうされていますでしょうか? ご意見を下されば幸いです。よろしくお願いします。

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

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

もし、オブジェクトの割付け・解放を繰り返すようなものでなければ、malloc等を使うのはやめ、静的記憶域期間のオブジェクトにする方が無難です。 また、割付け・解放を繰り返す場合でも、用途を限定できるのであれば、メモリプールを自作した方が安全な実装ができることもあります。 もう一点、割付けたオブジェクトのポインタが後から分かるのであれば(#1の方が書かれているようなリスト構造になっていたり、グローバルだったりする場合)、atexitで後始末用の関数を登録しておくのも一つの手です。

febrero217
質問者

お礼

動的にメモリ割付けを行っているオブジェクトは、あらかじめ大きさが決定できないものです。(コマンドライン引数、データファイルの中身、標準入力で決定)。 そして、それらの共通性、サイズの大小は色々です.(小さいものは strdup でファイル名だけ格納とか) また、一度サイズが決定すれば、後はサイズが変わらないものと、割り付け・解放を繰り返すものがあります. もう一度、記憶領域やスコープの違い等を整理して、考え直さなければならないようです。(楽しみです) メモリプール、atexit は全く新規のアイデア、知識です。 いきなり大きな世界が開けました。 時間をかけて勉強したいと思います。 ありがとうございました。

その他の回答 (7)

  • t_nojiri
  • ベストアンサー率28% (595/2071)
回答No.8

#1です。 ラッパーとは、【wrapper】です。 http://d.hatena.ne.jp/keyword/%a5%e9%a5%c3%a5%d1%a1%bc?kid=82295 つまり、alloc(malloc含む)系とfree系の処理を自前で用意(中身は、ポインタドレス管理と、管理したアドレスの開放処理を実装)する意味で書きました。

febrero217
質問者

お礼

’ラッパー’のごく単純な意味は何となく分かりました。 用法の中で把握しないといけない概念かな、と感じました。 今の問題に即したご説明は、問題解決のために活用させていたただきます。 どうもありがとうございました。

  • a-saitoh
  • ベストアンサー率30% (524/1722)
回答No.7

おそらく質問者が作っているプログラムはWindowsやUNIXなどのアプリケーションプログラム、つまり、プログラムをexit()で終了させるとそのプログラムを動かしていたプロセスも終了するという状況だと思います。その場合は、exit時のfreeは不要です。プロセスが使っていたメモリ(mallocで得たメモリはその一部です)はプロセス終了時にOSに返されますので。  ただし、プログラミングのお作法として,freeすべきかどうかというのはよく口げんかの元になるテーマです。巻き込まれないようにお気をつけください。 もう一つ、今作っているプログラムを将来サブルーチン化して、別のプログラムの中の一部品として使うような可能性がある(当然exit()はreturnに置き換えるなど修正が必要ですが)なら、エラー→処理中断という場合にもちゃんとfreeしないといけません。まぁ、(C++ならともかく)Cではそういうことななさそうですが。  Cプログラミングでエラーで処理を中断して終了する場合でも後始末(解放)が必要なのは、SystemV共有メモリとか、SystemVセマフォとか、です。

febrero217
質問者

お礼

ご回答を読ませていただいて、ひとまず安心しました。 とりあえず、現在のソースをそのまま使用して、 皆さんに教えていただいた広い世界を、ゆっくり勉強する、という方針が立ちました。 >freeすべきかどうかというのはよく口げんかの元になるテーマです。巻き込まれないように 周りに C でプログラミングをしている人間がいませんが、人に見せても恥ずかしくないソースを書けるようになりたいです。 再利用を考えれば、ちゃんと free すべきということも了解しました。 どうもありがとうございました。 皆さま、お礼が遅れまして申し訳ありませんでした。

  • MrBan
  • ベストアンサー率53% (331/615)
回答No.6

> 環境・コンパイラは Windows2000、cygwin の gcc です。 一応、この環境であればプロセス終了時にメモリは開放されます。 > 関数呼び出しのネストを一つづつ抜けて、こまめに解放するようにするか、迷うところです。 要件しだいですが、一般には使うところで開放する(こまめに開放)の方が多いと思います。 (全体で使うような共通データはまとめて確保かもしれませんが) これも実際の用法しだいですが、共通データは静的にしてしまい、他のデータは自動変数にしてしまう(動的確保を減らす)のも有効かもしれません。 こまめに管理する場合、C言語だと主に2方式が主流でしょうか。 参考URLの例で言えば、 void *tmp = malloc( size ); sub( tmp, x ); free( tmp ); か、その直下のgotoか。参考URLの例を読んでみてください。 開放漏れを防ぐためには、確保と開放の対比が重要です。(C言語の場合) # C++だと、コンストラクタで確保しておいて、 # 開放はデストラクタで自動化するイディオム(RAII)がありますけど。

参考URL:
http://www.nurs.or.jp/~sug/soft/super/goto.htm
febrero217
質問者

お礼

たいへん有用なアドバイスと、URLのご紹介をいただきました。 変数の種類、確保と開放の対比(良いソースとはどんなものか、はじめて触れた気がします)をもう一度考え直すことにします。 ご紹介いただいた URLには、他にもたくさん興味深い記事が見つかりました。 とても奥が深そうなので、勉強に時間がかかりそうですが、楽しみです。 見知らぬ専門家の方々に、貴重な知識を教わっているということに改めて驚いております。 どうもありがとうございました。

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

標準規格を読む限りでは、malloc等で割付けた割付け記憶域期間を持つオブジェクトの生存期間は、解放されるまでとなっており、プログラムが終了したかどうかは直接関係ありません。 また、main関数からのreturnやexit関数の呼び出しでは、制御をホスト環境に戻すだけで、動的に割付けたオブジェクトが解放されるかどうかは規定されていません。 つまり、制御を取り戻したホスト環境(オペレーティングシステムとほぼ同義)がメモリを解放するかどうかはホスト環境の仕様であり、C言語の規格外のことです。 質問者さんは環境を特定されていませんから、一般論で言えば、割付けたメモリは必ず解放しなければならないとしか回答できません。

febrero217
質問者

お礼

jactaさま、ご回答ありがとうございます。 環境・コンパイラは Windows2000、cygwin の gcc です。 ご回答を読ませていただいて、ちょっと恐ろしく感じましたので、きちんとメモリを解放しようと思います。 他の方に教えていただいたように、解放すべきメモリをまとめて管理するようにするか、関数呼び出しのネストを一つづつ抜けて、こまめに解放するようにするか、迷うところです。 専門家の方々の意見が聞けて、うれしいです。 どうもありがとうございました。

  • MrBan
  • ベストアンサー率53% (331/615)
回答No.3

メモリが正しく開放されるか否かは環境(OSやCRT)依存。 どの程度の精度が必要か、どの環境かによっても変わりますがが、 精度がいらない捨てツールなら、時にありかもしれません。(C言語の場合) # とはいえ、私自身はまずやりません。 # C++だと、デストラクタが呼ばれなかったりすることもあるのでむやみなexitは危険です。 # デストラクタで外部資源の解放してたりすると、OSは関知してくれないですし。

febrero217
質問者

お礼

ご回答ありがとうございます。 必要な精度がどの位か、どう言えばよいかわかりませんが、製品のような精度は求めません。 ただ、作ったツールは周囲の数人の人に使ってもらう可能性がありますし、捨てツールというわけではないです。 ># とはいえ、私自身はまずやりません。 専門家の方の認識が分かって、勉強になります。 ># C++だと、デストラクタが呼ばれなかったりすることもあるのでむやみなexitは危険です。 C++ もいずれ勉強したいと思っていますので,ためになる情報です。 やはり、きちんと管理したほうがよさそうだという考えになってます。 どうもありがとうございました。

  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.2

動的に確保したメモリはプロセス終了時にOSが解放してくれます。 従って、exitで終了することが明白な場合は、あえてfreeする必要はありません。 しかしながら、exitで終了しないケースも今後、多々でてくるでしょうから、確保したメモリは、不要になった時に、必ず、解放する癖をつけておいた方が良いでしょう。世間一般に数あるバグの一つが、メモリの解放もれによるものです。

febrero217
質問者

お礼

ご回答を拝見しまして、少し安心しましたが、他の回答者の方の言葉と合わせて考えると,逆にOSがきちんと解放してくれるかどうかが不安なところですね。 >必ず、解放する癖をつけておいた方が良いでしょう。 メモリの解放を忘れがちです。お言葉感謝します。 ありがとうございました。

  • t_nojiri
  • ベストアンサー率28% (595/2071)
回答No.1

煩雑になるって言っても、freeすべきはポインタのアドレスのみじゃないですか? allocした時のアドレスを、ラッパーしてリスト構造等で管理しとけばfree大した問題にならない筈な気がしますけど。 まあ、簡単なプログラムや永続的な物が無くて、絶対プロセス残らないツール等であれば、わざとfree書かない事も有りますけど。

febrero217
質問者

お礼

ご回答ありがとうございます。 >allocした時のアドレスを、ラッパーしてリスト構造等で管理 目から鱗のアイデアです。”ラッパー”の意味がはっきり分かりませんが、勉強して理解したいと思います。 プログラムは永続的なものではありませんが、簡単かどうか、絶対プロセスが残らないツールかどうかは、分かりません。 お教えいただいたアイデアを検討したいと思います。 ありがとうございました。

関連するQ&A

  • メモリ開放

    C#でのメモリの開放の仕方を教えてください。 二次元配列で1万、1万でやっているのですが、エラーが出てメモリが足りないと言われます。 開放をしたいのですが、ネットで探しても難しく書いてあって分かりません。おしえてください。

  • C言語でmain関数でのreturnとexit

    C言語でmain関数でのreturnとexitは同等とされてますが、 それは『プログラムを呼び出した元の動作』も同じですか? 例えばshellやOSから見て、割り当てメモリ開放などの点で returnで終了したプログラムの後処理と exitで終了したプログラムの後処理は 全く同じになりますか?

  • EXIT関数のGcc3.2.3でのコンパイルエラー

    mainで異常終了するときにexit関数で終了するHP-UX Cのプログラムがあります。 これをGcc3.2.3でコンパイルすると下記のエラーとなります. これをコンパイルエラーとしない方法を教えてください。 C言語の書物上は、引数なしでもOKなのですが。 「関数 `exit' への引数が少なすぎます」

  • exitってどう使うの?

    exitを使いたいのですが、プログラムが終了しません。 コンパイルも通りません。 警告:コードは効果を持たない(関数 exit) エラー:ステートメントにセミコロンが無い(関数 exit) と出ます。 windowsでC言語書いています。 入力した数値分「警告」を表示するプログラムなのですが #include <stdio.h> #include <stdlib.h> void exit(int x) { while(x-- > 0){ if(x==0){ printf("警告!\n"); exit 0; } printf("警告!残り%d回。\n", x); } } int main() //警告を表示する回数を変数に代入するプログラムvoid exitのxに渡す。 どうすればexitの位置でプログラムが終了するようにできるのでしょうか。 どなたか教えていただけないでしょうか。

  • GDI+におけるメモリの開放について

    C++でGDI+のコードを書いています。 たとえば Gdiplus::Bitmap bmp(1000, 1000); を一度呼ぶと、アプリケーションを終わらすまで確保した領域が開放されず、 繰り返し呼ぶと使用メモリがどんどん増えていってしまいます。 確保したBitmapを開放するにはどうしたらよいのでしょうか?

  • Linuxでexit()をフックするには?

    glib内のある関数のパフォーマンスを調べるために、 ログを埋め込もうとしています。 手法としては、あらかじめメモリ領域を確保しておき、 glibc内でメモリ上にログを吐き出し、 glicを使用するアプリケーション終了時(exitシステムコール実行時)に、 ログを出力しようと思います。 そこで質問なのですが、 Linuxのglibc(2.3.3)でexit()をフックして、 自前の関数を呼び出す方法はありますか? ご存知なら教えてください。

  • メモリの開放を全て行いたいが開放し損ねている VC++6.0 使用

    こんにちは。 Win2000環境 VC++6を使用してあるアプリケーションを開発してまして そのアプリ終了時に、確保したメモリを開放しているのですが 「すべてのメモリを開放できませんでした」というような表示がされてしまいます。 (この表示文字自体は、そのアプリの仕様です。エラーチェックしてくれています) 自分ではmallocしたデータは全て開放しているつもりですが どのメモリ(変数)が開放し損ねているのかわからずに困っています。 VCのデバッグモードを使用して処理をたどってみても、場所が特定できません。 mallocしている部分をコメントアウト等して ちょっとづつでも場所を特定できればいいのですが、 諸事情により(プログラムの処理上)それができない形になっています。 皆さんは、メモリを開放させる処理で、どこかメモリを開放し損ねている場合 どのようにその場所を突き止めているのでしょうか? VC++のデバッグモードの何かしらの機能か何かで その場所を表示させるような事なのできないのでしょうか? ちょっとわかりにくい説明ですが、 ご存知の方、どうぞよろしくお願い致します。

  • exitとreturnの使い分け

    通常の開発でexitは使ってもいいのでしょうか? main以外の関数でbool型でmainに処理を戻しプログラムを終了させるのと、関数内でexitを使い処理を終了させることの違いと具体的にどういう場合に使い分けたらいいのかを教えてください。 できるだけ詳しく教えてもらえるとうれしいです。

  • メモリ破壊で困っています。

    学生です。 現在、cの課題プログラムを作成していて、メモリ破壊と思われる現象で困っています。具体的には、 mallocである構造体へのポインタの3次元配列を確保したはずのものが(malloc時にNULLは返ってきていない)、その後、関係のない関数を呼んだ瞬間にその配列の値が書き換えられている。もしくはアクセスできなくなるといった状況です。 gdbで調べてみたところメモリを確保してから破壊されるまでにfreeはしていません。「関数を呼んだ瞬間に」値が変わるというのは原因がまったくわかりません。 どなたか心当たりある方、ぜひともアドバイスをよろしくお願いします。

  • 動的メモリとexit(C言語)

    fpA=fopen("name1", "r"); fpB=fopen("name2", "r"); fpC=fopen("name3", "r"); if(fpA==NULL || fpB==NULL || fpC==NULL){ exit(1); } fpAとfpBのファイルオープンに成功してfpCで失敗した場合、exit(1)によってfpAとfpBはクローズされますよね? じゃあ、 a=malloc(size); b=malloc(size); c=malloc(size); if(a==NULL || b==NULL || c==NULL){ exit(1); } aとbのメモリ確保に成功してcで失敗した場合、aとbのメモリはどうなっちゃうんでしょう?exitは動的メモリも解放してくれるのですか? とりあえず以下のようにしてますが…。 a=malloc(size); b=malloc(size); c=malloc(size); if(a==NULL || b==NULL || c==NULL){ free(a); free(b); free(c); exit(1); }

専門家に質問してみよう