- ベストアンサー
エラー処理,メモリ開放,exit
こんばんは。現在、次のような C のプログラムを作っています。 ・ 動的にメモリを確保した配列や構造体が複数ある。 ・ しばしば,関数呼び出しのネストが複数(2~4) になる。 ・ ”エラーチェック => エラーなら終了” という動作が多い。 このようなプログラムで,"エラー => プログラム終了" の際,動的に確保したメモリを,関数呼び出しのネストを(return文 で)遡って,全て開放すべきでしょうか。 このようにすると,あまりにプログラムが煩雑になるので,今は "エラー => exit(-1) で直ちに終了" としています。 しかし改めて C の教科書を見ると,exit の時,オープンされているファイルやストリームについては,捨てられ,あるいは閉じられる,と書いてありますが,メモリについては何も書かれていないので,どうするべきか少し悩んでいます。 みなさんはこんな場合,どうされていますでしょうか? ご意見を下されば幸いです。よろしくお願いします。
- みんなの回答 (8)
- 専門家の回答
質問者が選んだベストアンサー
もし、オブジェクトの割付け・解放を繰り返すようなものでなければ、malloc等を使うのはやめ、静的記憶域期間のオブジェクトにする方が無難です。 また、割付け・解放を繰り返す場合でも、用途を限定できるのであれば、メモリプールを自作した方が安全な実装ができることもあります。 もう一点、割付けたオブジェクトのポインタが後から分かるのであれば(#1の方が書かれているようなリスト構造になっていたり、グローバルだったりする場合)、atexitで後始末用の関数を登録しておくのも一つの手です。
その他の回答 (7)
- t_nojiri
- ベストアンサー率28% (595/2071)
#1です。 ラッパーとは、【wrapper】です。 http://d.hatena.ne.jp/keyword/%a5%e9%a5%c3%a5%d1%a1%bc?kid=82295 つまり、alloc(malloc含む)系とfree系の処理を自前で用意(中身は、ポインタドレス管理と、管理したアドレスの開放処理を実装)する意味で書きました。
お礼
’ラッパー’のごく単純な意味は何となく分かりました。 用法の中で把握しないといけない概念かな、と感じました。 今の問題に即したご説明は、問題解決のために活用させていたただきます。 どうもありがとうございました。
- a-saitoh
- ベストアンサー率30% (524/1722)
おそらく質問者が作っているプログラムはWindowsやUNIXなどのアプリケーションプログラム、つまり、プログラムをexit()で終了させるとそのプログラムを動かしていたプロセスも終了するという状況だと思います。その場合は、exit時のfreeは不要です。プロセスが使っていたメモリ(mallocで得たメモリはその一部です)はプロセス終了時にOSに返されますので。 ただし、プログラミングのお作法として,freeすべきかどうかというのはよく口げんかの元になるテーマです。巻き込まれないようにお気をつけください。 もう一つ、今作っているプログラムを将来サブルーチン化して、別のプログラムの中の一部品として使うような可能性がある(当然exit()はreturnに置き換えるなど修正が必要ですが)なら、エラー→処理中断という場合にもちゃんとfreeしないといけません。まぁ、(C++ならともかく)Cではそういうことななさそうですが。 Cプログラミングでエラーで処理を中断して終了する場合でも後始末(解放)が必要なのは、SystemV共有メモリとか、SystemVセマフォとか、です。
お礼
ご回答を読ませていただいて、ひとまず安心しました。 とりあえず、現在のソースをそのまま使用して、 皆さんに教えていただいた広い世界を、ゆっくり勉強する、という方針が立ちました。 >freeすべきかどうかというのはよく口げんかの元になるテーマです。巻き込まれないように 周りに C でプログラミングをしている人間がいませんが、人に見せても恥ずかしくないソースを書けるようになりたいです。 再利用を考えれば、ちゃんと free すべきということも了解しました。 どうもありがとうございました。 皆さま、お礼が遅れまして申し訳ありませんでした。
- MrBan
- ベストアンサー率53% (331/615)
> 環境・コンパイラは Windows2000、cygwin の gcc です。 一応、この環境であればプロセス終了時にメモリは開放されます。 > 関数呼び出しのネストを一つづつ抜けて、こまめに解放するようにするか、迷うところです。 要件しだいですが、一般には使うところで開放する(こまめに開放)の方が多いと思います。 (全体で使うような共通データはまとめて確保かもしれませんが) これも実際の用法しだいですが、共通データは静的にしてしまい、他のデータは自動変数にしてしまう(動的確保を減らす)のも有効かもしれません。 こまめに管理する場合、C言語だと主に2方式が主流でしょうか。 参考URLの例で言えば、 void *tmp = malloc( size ); sub( tmp, x ); free( tmp ); か、その直下のgotoか。参考URLの例を読んでみてください。 開放漏れを防ぐためには、確保と開放の対比が重要です。(C言語の場合) # C++だと、コンストラクタで確保しておいて、 # 開放はデストラクタで自動化するイディオム(RAII)がありますけど。
お礼
たいへん有用なアドバイスと、URLのご紹介をいただきました。 変数の種類、確保と開放の対比(良いソースとはどんなものか、はじめて触れた気がします)をもう一度考え直すことにします。 ご紹介いただいた URLには、他にもたくさん興味深い記事が見つかりました。 とても奥が深そうなので、勉強に時間がかかりそうですが、楽しみです。 見知らぬ専門家の方々に、貴重な知識を教わっているということに改めて驚いております。 どうもありがとうございました。
- jacta
- ベストアンサー率26% (845/3158)
標準規格を読む限りでは、malloc等で割付けた割付け記憶域期間を持つオブジェクトの生存期間は、解放されるまでとなっており、プログラムが終了したかどうかは直接関係ありません。 また、main関数からのreturnやexit関数の呼び出しでは、制御をホスト環境に戻すだけで、動的に割付けたオブジェクトが解放されるかどうかは規定されていません。 つまり、制御を取り戻したホスト環境(オペレーティングシステムとほぼ同義)がメモリを解放するかどうかはホスト環境の仕様であり、C言語の規格外のことです。 質問者さんは環境を特定されていませんから、一般論で言えば、割付けたメモリは必ず解放しなければならないとしか回答できません。
お礼
jactaさま、ご回答ありがとうございます。 環境・コンパイラは Windows2000、cygwin の gcc です。 ご回答を読ませていただいて、ちょっと恐ろしく感じましたので、きちんとメモリを解放しようと思います。 他の方に教えていただいたように、解放すべきメモリをまとめて管理するようにするか、関数呼び出しのネストを一つづつ抜けて、こまめに解放するようにするか、迷うところです。 専門家の方々の意見が聞けて、うれしいです。 どうもありがとうございました。
- MrBan
- ベストアンサー率53% (331/615)
メモリが正しく開放されるか否かは環境(OSやCRT)依存。 どの程度の精度が必要か、どの環境かによっても変わりますがが、 精度がいらない捨てツールなら、時にありかもしれません。(C言語の場合) # とはいえ、私自身はまずやりません。 # C++だと、デストラクタが呼ばれなかったりすることもあるのでむやみなexitは危険です。 # デストラクタで外部資源の解放してたりすると、OSは関知してくれないですし。
お礼
ご回答ありがとうございます。 必要な精度がどの位か、どう言えばよいかわかりませんが、製品のような精度は求めません。 ただ、作ったツールは周囲の数人の人に使ってもらう可能性がありますし、捨てツールというわけではないです。 ># とはいえ、私自身はまずやりません。 専門家の方の認識が分かって、勉強になります。 ># C++だと、デストラクタが呼ばれなかったりすることもあるのでむやみなexitは危険です。 C++ もいずれ勉強したいと思っていますので,ためになる情報です。 やはり、きちんと管理したほうがよさそうだという考えになってます。 どうもありがとうございました。
- tatsu99
- ベストアンサー率52% (391/751)
動的に確保したメモリはプロセス終了時にOSが解放してくれます。 従って、exitで終了することが明白な場合は、あえてfreeする必要はありません。 しかしながら、exitで終了しないケースも今後、多々でてくるでしょうから、確保したメモリは、不要になった時に、必ず、解放する癖をつけておいた方が良いでしょう。世間一般に数あるバグの一つが、メモリの解放もれによるものです。
お礼
ご回答を拝見しまして、少し安心しましたが、他の回答者の方の言葉と合わせて考えると,逆にOSがきちんと解放してくれるかどうかが不安なところですね。 >必ず、解放する癖をつけておいた方が良いでしょう。 メモリの解放を忘れがちです。お言葉感謝します。 ありがとうございました。
- t_nojiri
- ベストアンサー率28% (595/2071)
煩雑になるって言っても、freeすべきはポインタのアドレスのみじゃないですか? allocした時のアドレスを、ラッパーしてリスト構造等で管理しとけばfree大した問題にならない筈な気がしますけど。 まあ、簡単なプログラムや永続的な物が無くて、絶対プロセス残らないツール等であれば、わざとfree書かない事も有りますけど。
お礼
ご回答ありがとうございます。 >allocした時のアドレスを、ラッパーしてリスト構造等で管理 目から鱗のアイデアです。”ラッパー”の意味がはっきり分かりませんが、勉強して理解したいと思います。 プログラムは永続的なものではありませんが、簡単かどうか、絶対プロセスが残らないツールかどうかは、分かりません。 お教えいただいたアイデアを検討したいと思います。 ありがとうございました。
お礼
動的にメモリ割付けを行っているオブジェクトは、あらかじめ大きさが決定できないものです。(コマンドライン引数、データファイルの中身、標準入力で決定)。 そして、それらの共通性、サイズの大小は色々です.(小さいものは strdup でファイル名だけ格納とか) また、一度サイズが決定すれば、後はサイズが変わらないものと、割り付け・解放を繰り返すものがあります. もう一度、記憶領域やスコープの違い等を整理して、考え直さなければならないようです。(楽しみです) メモリプール、atexit は全く新規のアイデア、知識です。 いきなり大きな世界が開けました。 時間をかけて勉強したいと思います。 ありがとうございました。