C ++コードを終了する方法


267

特定の条件が満たされた場合にC ++コードの実行を停止したいのですが、その方法がわかりません。したがって、いつでもifステートメントがtrueの場合は、次のようにコードを終了します。

if (x==1)
{
    kill code;
}

98
適切なロジックを使用してください。でmain()使用リターン、機能に適切な戻り値を使用するか、適切な例外をスローします。使用しないでくださいexit()
わかりました-SEは邪悪です

6
@JonathanLeffler:NDEBUG定義済みでコンパイルすると、assertおそらく何も行われなくなります。そのため、バグの検出以外に依存しないでください。
MvG

13
@jamesqf:returnfrom main()は完全に論理的です。これは、プログラムによって報告された終了コードをオペレーティングシステムに設定します。これは、多くの場合に役立ちます(たとえば、プログラムがより大きなプロセスの一部として使用できる場合)。
AAT、2015年

11
興味深いことに、このQは受け入れ答えはかなり異なっている5年前から、この1、の重複がある:stackoverflow.com/questions/1116493/how-to-quit-ac-program私はそれが盲目的にそれを学ぶために5年間のコミュニティを取ったと思いますstd :: exitを呼び出すのは悪いことですか?
Brandin

5
質問なぜ使用がexit()悪いと考えられていますか?– SO 25141737およびC ++プログラムを終了する方法?– SO 1116493は、これの複製としてクローズされました。それらのうちの最初のものは、堅牢なソフトウェアの書き方についてのあなたの考えを刺激するためにだけであるなら、一見に値するかもしれない「クラッシュのみのソフトウェア」への参照を持っています。
ジョナサンレフラー

回答:


431

いくつかの方法がありますが、最初にオブジェクトのクリーンアップが重要である理由を理解する必要がありstd::exitます。そのため、C ++プログラマーの間ではその理由は疎外されます。

RAIIとスタックの巻き戻し

C ++はRAIIと呼ばれるイディオムを使用します。これは、簡単に言えば、オブジェクトはコンストラクターで初期化を実行し、デストラクターでクリーンアップを実行する必要があることを意味します。たとえば、std::ofstreamクラスはコンストラクターの間にファイルを開く可能性があり、ユーザーはそれに対して出力操作を実行し、最後に通常はそのスコープによって決定されるそのライフサイクルの最後に、デストラクターが呼び出されて本質的にファイルを閉じてフラッシュしますディスクに書き込まれたコンテンツ。

デストラクタがファイルをフラッシュして閉じない場合はどうなりますか?知るか!ただし、ファイルに書き込むはずのデータがすべて書き込まれるとは限りません。

たとえば、このコードを検討してください

#include <fstream>
#include <exception>
#include <memory>

void inner_mad()
{
    throw std::exception();
}

void mad()
{
    auto ptr = std::make_unique<int>();
    inner_mad();
}

int main()
{
    std::ofstream os("file.txt");
    os << "Content!!!";

    int possibility = /* either 1, 2, 3 or 4 */;

    if(possibility == 1)
        return 0;
    else if(possibility == 2)
        throw std::exception();
    else if(possibility == 3)
        mad();
    else if(possibility == 4)
        exit(0);
}

それぞれの可能性で何が起こるかです:

  • 可能性1: Returnは基本的に現在の関数スコープを離れるので、ライフサイクルの終わりを知っているため、osそのデストラクタを呼び出し、ファイルを閉じてディスクにフラッシュすることによって適切なクリーンアップを実行します。
  • 可能性2:例外をスローすると、現在のスコープ内のオブジェクトのライフサイクルも処理され、適切なクリーンアップが行われます...
  • 可能性3:スタックの巻き戻しが実行されます!例外がスローでされていてもinner_mad、アンワインダーはスタックものの行くmadmain適切なクリーンアップを実行するために、すべてのオブジェクトには、適切に破壊しようとしているptros
  • 可能性4:まあ、ここ?exitC関数であり、C ++イディオムを認識しておらず、互換性もありません。非常に同じスコープを含む、オブジェクトのクリーンアップ実行されませんos。したがって、ファイルが適切に閉じられないため、コンテンツがファイルに書き込まれない可能性があります。
  • その他の可能性:暗黙的に実行し、return 0可能性1と同じ効果、つまり適切なクリーンアップを行うことで、メインスコープを離れます。

しかし、私があなたに今言った内容(主に可能性2と3)についてはそれほど確信してはいけません。読み続けて、適切な例外ベースのクリーンアップを実行する方法を見つけます。

終了する可能な方法

メインから戻ります!

可能な限りこれを行う必要があります。mainから適切な終了ステータスを返すことにより、常にプログラムから戻ることを好みます。

プログラムの呼び出し元、および場合によってはオペレーティングシステムは、プログラムが実行するはずの処理が正常に行われたかどうかを知りたい場合があります。これと同じ理由で、ゼロを返すかEXIT_SUCCESS、プログラムが正常に終了したことを通知し、プログラムが正常に終了しEXIT_FAILUREなかったことを通知する必要があります。他の形式の戻り値は実装定義です(§18.5/ 8)。

ただし、コールスタックが非常に深く、すべてを返すのは面倒な場合があります...

例外を投げる[しない]

例外をスローすると、以前のスコープ内のすべてのオブジェクトのデストラクタを呼び出すことにより、スタックの巻き戻しを使用して適切なオブジェクトのクリーンアップを実行します。

しかし、ここに問題があります!スローされた例外が(catch(...)句によって)処理されない場合、またはnoexcept呼び出しスタックの途中に関数がある場合でも、スタックの巻き戻しが実行されるかどうかは、実装によって定義されます。これは§15.5.1[except.terminate]で述べられています

  1. 状況によっては、エラー処理技術の微妙さを減らすために、例外処理を中止する必要があります。[注:これらの状況は次のとおりです。

    [...]

    - ときのメカニズムを扱う例外がスローされた例外(15.3)のハンドラを見つけることができない場合、またはハンドラ(15.3)の検索が持つ機能の最も外側のブロックに遭遇noexcept-specification例外を許可しない(15.4)、または[...]

    [...]

  2. そのような場合、std :: terminate()が呼び出されます(18.8.3)。一致するハンドラーが見つからない場合、std :: terminate()が呼び出される前にスタックが巻き戻されるかどうかは実装定義です[...]

だから私たちはそれをキャッチする必要があります!

例外をスローしてメインでキャッチしてください!

キャッチされない例外はスタックの巻き戻しを実行しない可能性があるため(したがって、適切なクリーンアップは実行されません)、メインで例外をキャッチしてから、終了ステータス(EXIT_SUCCESSまたはEXIT_FAILURE)を返す必要があります。

したがって、おそらく良い設定は次のようになります。

int main()
{
    /* ... */
    try
    {
        // Insert code that will return by throwing a exception.
    }
    catch(const std::exception&)  // Consider using a custom exception type for intentional
    {                             // throws. A good idea might be a `return_exception`.
        return EXIT_FAILURE;
    }
    /* ... */
}

std :: exit [するな]

これは、いかなる種類のスタックの巻き戻しも実行せず、スタック上の生きているオブジェクトは、それぞれのデストラクタを呼び出してクリーンアップを実行しません。

これは、§3.6.1/ 4 [basic.start.init]で実施されています。

現在のブロックを離れずにプログラムを終了しても(たとえば、関数std :: exit(int)(18.5)を呼び出して)、自動保存期間(12.4)のオブジェクトは破棄されません。静的またはスレッドストレージ期間のオブジェクトの破棄中にstd :: exitを呼び出してプログラムを終了すると、プログラムの動作は未定義になります。

今考えてみてください、なぜそんなことをするのですか?痛いほど多くのオブジェクトを損傷しましたか?

その他の[悪い]代替案

プログラムを終了する方法は他にもあります(クラッシュ以外)が、お勧めできません。明確にするために、ここではそれらを紹介します。お知らせどのように通常のプログラムの終了が ない平均スタックの巻き戻しが、大丈夫オペレーティングシステムの状態を。

  • std::_Exit 通常のプログラム終了を引き起こし、それだけです。
  • std::quick_exit通常のプログラムの終了を引き起こし、std::at_quick_exitハンドラーを呼び出します。他のクリーンアップは実行されません。
  • std::exitプログラムを正常に終了させ、std::atexitハンドラーを呼び出します。静的オブジェクトデストラクタの呼び出しなど、他の種類のクリーンアップが実行されます。
  • std::abortプログラムの異常終了を引き起こし、クリーンアップは実行されません。これは、プログラムが本当に予期しない方法で終了した場合に呼び出す必要があります。異常終了についてOSに通知するだけです。この場合、一部のシステムはコアダンプを実行します。
  • std::terminateデフォルトではstd::terminate_handlerwhichが呼び出しstd::abortます。

25
これは非常に有益です:特に、どこでも処理されない例外(たとえば、一部のnewスローstd::bad_allocとプログラムがどこでその例外をキャッチするのを忘れた)をスローしても、終了する前にスタックを適切に巻き戻さないことに気づきませんでした。これは私にはばかげているように見えます:呼び出しを基本的に簡単なものにラップすることmainは簡単だったでしょうtry{- }catch(...){}そのような場合にスタックの巻き戻しが無料で適切に行われることを保証するブロック(つまり、これを使用しないプログラムはお金を払わないでしょう)ペナルティ)。これが行われない特別な理由はありますか?
マルクファンレーウェン2015年

12
@MarcvanLeeuwen考えられる理由の1つはデバッグです。未処理の例外がスローされたらすぐにデバッガーに侵入したいとします。スタックをほどいてクリーンアップを行うと、デバッグしたいクラッシュの原因のコンテキストが消去されます。デバッガーが存在しない場合は、コアをダンプして、事後分析を実行できるようにすることをお勧めします。
ヒューアレン、

11
時々、私のブラウザーはバグを起こし(私はフラッシュのせいです)、数ギガバイトのRAMを消費し、私のOSはハードドライブのページをダンプしてすべてを遅くします。ブラウザを閉じると、適切なスタックの巻き戻しが行われます。つまり、ギガバイトのRAMがすべてハードドライブから読み込まれ、メモリにコピーされて解放されるだけです。これには1分ほどかかります。std::abortOSが1分間スワッピングすることなくすべてのメモリ、ソケット、ファイル記述子を解放できるように、代わりにそれらが使用されていれば幸いです。
nwp

2
@nwp、私は気持ちを理解しています。しかし、即座に殺すとファイルが破損し、最新のタブなどが保存されない可能性があります:)
Paul Draper

2
@PaulDraper電源が切れる前に開いていたタブをブラウザーが復元できなかった場合、私は確かにそれを許容できるとは考えません。もちろん、タブを開いたばかりで、まだ保存する時間がなかった場合、失われます。しかし、それ以外には、それを失うことの言い訳はないと言います。
kasperd

61

Martin Yorkが述べたように、exitはreturnのように必要なクリーンアップを実行しません。

出口の代わりにリターンを使用することは常により良いです。メインにいない場合は、プログラムを終了したい場所で、まずメインに戻ります。

以下の例を考えてみましょう。以下のプログラムで、記載された内容でファイルが作成されます。ただし、returnがコメント化されており、コメント化されていないexit(0)の場合、コンパイラーはファイルに必要なテキストが含まれることを保証しません。

int main()
{
    ofstream os("out.txt");
    os << "Hello, Can you see me!\n";
    return(0);
    //exit(0);
}

これだけでなく、プログラムに複数の出口点があると、デバッグが困難になります。正当化できる場合にのみexitを使用してください。


2
少し大きいプログラムでこの動作を実現するために何をお勧めしますか?コードのどこかでプログラムを終了する必要があるエラー条件がトリガーされた場合、常にメインにきれいに戻るにはどうすればよいですか?
Janusz

5
@Janusz、その場合、事前定義された値を返さない場合、つまり、たとえば成功した場合は0を返し、失敗した場合は1を返しますが、実行を継続するなど、関数からの戻り値がある場合は、例外を使用/スローできます。 、失敗した場合は-1、プログラムを終了します。関数からの戻り値に基づいて、それが失敗した場合は、さらにクリーンアップアクティビティを実行した後、メインから戻ります。最後に、exitは慎重に使用してください。回避するつもりはありません。
Narendra N

1
@NarendraN、「必要なクリーンアップ」はあいまいです-OSはメモリとファイルハンドルが適切に解放されるように注意します(Windows / Linux)。「欠落している」ファイル出力について:これが本当の問題である可能性があると主張する 場合は、stackoverflow.com / questions / 14105650 / how-does-stdflush-workを参照してください。エラー状態がある場合、適切なロギングにより、プログラムがが未定義の状態に達したため、ロギングポイントの直前にブレークポイントを設定できます。それはどのようにデバッグを難しくするのですか?
Markus

2
この回答は、「C ++プログラムを終了する方法」という質問からマージされたことに注意してください– SO 1116493。この回答は、この質問が尋ねられる約6年前に書かれました。
Jonathan Leffler、2015

return (EXIT_SUCCESS);代わりに最新のC ++を使用 return(0)
Jonas Stein

39

std::exit関数を呼び出します。   


2
その関数を呼び出すと、どのオブジェクトのデストラクタが呼び出されますか?
ロブ・ケネディ

37
exit()は戻りません。したがって、スタックの巻き戻しは実行できません。グローバルオブジェクトでさえ破壊されません。しかし、atexit()で登録された関数が呼び出されます。
マーティンヨーク

16
exit()ライブラリコードが私のホストプロセス内で実行されているときは呼び出さないでください。後者はどこにもないところで終了します。
シャープトゥース

5
この回答は、「C ++プログラムを終了する方法」という質問からマージされたことに注意してください– SO 1116493。この回答は、この質問が尋ねられる約6年前に書かれました。
Jonathan Leffler、2015

23

人々は「exit(戻りコード)を呼ぶ」と言っていますが、これは悪い形です。小さなプログラムでは問題ありませんが、これには多くの問題があります:

  1. あなたはプログラムから複数の出口点を持つことになります
  2. コードを複雑にする(gotoを使用するなど)
  3. 実行時に割り当てられたメモリを解放できません

本当に、問題を終了する必要があるのはmain.cppの次の行だけです。

return 0;

エラーの処理にexit()を使用している場合は、よりエレガントで安全な方法として、例外(および例外のネスト)について学ぶ必要があります。


9
マルチスレッド環境では、別のスレッドでスローされた例外はmain()を介して処理されません-従属スレッドが期限切れになる前に、手動のスレッド間通信が必要です。
スティーブギルハム

3
1.と2.はプログラマ次第です。適切なロギングがあれば、実行が永久に停止するため、これは問題にはなりません。3:は間違いです。OSがメモリを解放します-おそらく組み込みデバイス/リアルタイムを除外しますが、そうしている場合、おそらくあなたは自分のことを知っています。
Markus 2014

1
この回答は、「C ++プログラムを終了する方法」という質問からマージされたことに注意してください– SO 1116493。この回答は、この質問が尋ねられる約6年前に書かれました。
Jonathan Leffler、2015

1
ただし、エラーが発生した場合は、0を返すべきではありません。1(または他の値かもしれませんが、1は常に安全な選択です)を返すべきです。
Kef Schecter

14

return 0;それを好きな場所に置くint main()と、プログラムはすぐに終了します。


the-nightman、@ evan-carslake、および他のすべての人、私はまた、このSOの質問#36707が、returnステートメントがルーチン内のどこかで発生するかどうかについての議論を提供することも追加します。これは解決策です。ただし、特定の状況によっては、これが最良の解決策であるとは言えません。
localhost

1
OPはこのことについて何も言わなかったので、私はコードが関数内であることを意味したと仮定するという発疹だと思いますmain
マルクファンレーウェン

@localhost returnステートメントは、どのような状況でも、あらゆる状況で完全にオプションです。ただし、int main()違います。プログラムはint main()、開始と終了に使用します。それを行う他の方法はありません。プログラムは、trueまたはfalseを返すまで実行されます。ほとんどの場合、プログラムが正しく閉じたことを示すために、1またはtrueを返します(たとえば、メモリを解放できなかった場合や、なんらかの理由でfalseを使用する場合があります)。プログラム自体を終了させることは常に悪い考えです。例:int main() { int x = 2; int foo = x*5; std::cout << "blah"; }
Evan Carslake、

@EvanCarslake了解しました。この質問に対して他の場所に投稿したコメントに気付いた場合、私はこれについてよく知っていint mainます。ただし、メインルーチンに複数のreturnステートメントを含めることは常に良い考えとは限りません。潜在的な注意点の1つは、コードを読みやすくすることです。ただし、状態を変更するブールフラグのようなものを使用して、コードの特定のコードセクションがそのアプリケーション状態で実行されないようにします。個人的には、共通のreturnステートメントにより、ほとんどのアプリケーションが読みやすくなります。最後に、メモリクリーンアップと、終了前にI / Oオブジェクトを適切に閉じることに関する質問。
localhost

2
あなたは戻りません@EvanCarslake trueからmain正しい終了を示すために。正常終了を示すにはEXIT_SUCCESS、ゼロまたは(必要に応じて、false暗黙的にゼロに変換される)を返す必要があります。失敗を示すために戻ることができますEXIT_FAILURE。その他のコードの意味は実装定義です(POSIXシステムでは、実際のエラーコードを意味します)。
ルスラン

11

プログラムは、実行フローがメイン関数の最後に到達すると終了します。

それより前に終了するには、exit(int status)関数を使用できます。ここで、statusはプログラムを開始したものに返される値です。0は通常、非エラー状態を示します


2
この回答は、「C ++プログラムを終了する方法」という質問からマージされたことに注意してください– SO 1116493。この回答は、この質問が尋ねられる約6年前に書かれました。
Jonathan Leffler


11

コードのどこかでエラーが発生した場合は、例外をスローするか、エラーコードを設定します。エラーコードを設定する代わりに、常に例外をスローする方が適切です。


2
この回答は、「C ++プログラムを終了する方法」という質問からマージされたことに注意してください– SO 1116493。この回答は、この質問が尋ねられる約6年前に書かれました。
Jonathan Leffler

9

通常exit()は、適切な終了ステータスでメソッドを使用します

ゼロは実行が成功したことを意味します。ゼロ以外のステータスは、何らかの問題が発生したことを示します。この終了コードは、プロセスが正常に実行されたかどうかを判断するために、親プロセス(シェルスクリプトなど)によって使用されます。


1
出口を使用すると、設計上の問題が発生する可能性があります。プログラムが正しく動作する場合は、mainのときに終了する必要がありますreturn 0;。私exit()assert(false);そうであり、問​​題を早期に発見するために開発でのみ使用されるべきだと思います。
CoffeDeveloper 2015年

2
この回答は、「C ++プログラムを終了する方法」という質問からマージされたことに注意してください– SO 1116493。この回答は、この質問が尋ねられる約6年前に書かれました。
Jonathan Leffler

7

exit(error_code)を呼び出す以外に-atexitハンドラーを呼び出すが、RAIIデストラクタなどは呼び出さない-ますます例外を使用しています。

ますます私のメインプログラムは次のようになります

int main(int argc, char** argv) 
{
    try {
        exit( secondary_main(argc, argv );
    }
    catch(...) {
        // optionally, print something like "unexpected or unknown exception caught by main"
        exit(1);
    }
}

where secondary_mainには、元々あったすべてのものが置かれます。つまり、元のメインは名前がsecondary_mainに変更され、上記のスタブメインが追加されます。これは非常に便利なので、トレイとメインキャッチの間にコードが多すぎません。

必要に応じて、他の例外タイプをキャッチします。
std :: stringやchar *などの文字列エラータイプをキャッチし、メインのキャッチハンドラーでそれらを出力するのがとても好きです。

このような例外を使用すると、少なくともRAIIデストラクタを呼び出すことができるため、クリーンアップを実行できます。どれが楽しくて便利なのでしょう。

全体として、Cエラー処理-終了とシグナル-およびC ++エラー処理-例外の試行/キャッチ/スロー-せいぜい一貫性のない状態で一緒にプレイします。

次に、エラーを検出した場所

throw "error message"

または、より具体的な例外タイプ。


ちなみに、メモリ不足に関連している可能性のある例外の例外ハンドラ内またはその周辺では、動的メモリ割り当てを伴う可能性のある文字列などのデータ型を使用することはお勧めしません。Cスタイルの文字列定数は問題ではありません。
Krazy Glew 2014

1
exitプログラムを呼び出す意味がありません。あなたがいるのでmain、あなたはちょうどできますreturn exitCode;
ルスラン

-1

ifステートメントがループ内にある場合

 break; 

コードをエスケープしてループを続行する場合は、次のように使用します。

継続する;

ifステートメントがループにない場合:

 return 0;

Or 




  exit();

-2

Dude ... exit()関数はstdlib.hで定義されています

したがって、プリプロセッサを追加する必要があります。

include stdlib.hヘッダーセクションに 入れる

次に、exit();好きな場所で使用しますが、出口の括弧内に整数を入れてください。

例えば:

exit(0);

-2

私がテストしている状態が本当に悪いニュースである場合、私はこれを行います:

*(int*) NULL= 0;

これにより、状況を調査できる優れたコアダンプが得られます。


4
これは未定義の動作を引き起こすため、最適化される可能性があります。実際、そのようなステートメントを持つ実行のブランチ全体は、コンパイラーによって一掃することができます。
ルスラン

-2

条件を解除するには、return(0)を使用します。

したがって、あなたの場合は次のようになります:

    if(x==1)
    {
        return 0;
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.