returnステートメントとmain()のexit()


197

私はステートメントを使用する必要がありますexit()か?個人的には、他の関数を読み取るようなものであり、コードを読んでいるときのフロー制御は(私の意見では)スムーズであると思うので、ステートメントを好みます。また、関数をリファクタリングしたい場合でも、を選択する方が良いと思います。returnmain()returnmain()returnexit()

ないexit()何も特別なんreturnではないんか?

回答:


277

実際に 違いがあります、微妙です。C ++にはより多くの影響がありますが、違いは重要です。

で呼び出すreturnmain()、ローカルスコープのオブジェクトに対してデストラクタが呼び出されます。私が呼び出した場合exit()何のデストラクタは私のローカルスコープのオブジェクトのために呼び出されません!もう一度読んでください。exit() 戻りません。つまり、私がそれを一度呼ぶと、「背中はありません」ということはありません。その関数で作成したオブジェクトは破棄されません。多くの場合、これは影響を与えませんが、ファイルを閉じるなど、場合によっては影響を及ぼします(すべてのデータをディスクにフラッシュする必要がありますか?)。

staticを呼び出してもオブジェクトはクリーンアップされることに注意してくださいexit()。最後に、を使用した場合abort()、オブジェクトは破棄されないことに注意してください。つまり、グローバルオブジェクト、静的オブジェクト、ローカルオブジェクトのデストラクタは呼び出されません。

リターンよりも出口を優先する場合は、慎重に進んでください。

http://groups.google.com/group/gnu.gcc.help/msg/8348c50030cfd15a


1
abort()はエラー条件(ゼロ以外の終了コード)で終了し、コアになることもあります。静的デストラクタを呼び出さずに終了する必要がある場合は、_exitを使用します。

7
@マイク:CライブラリファイルバッファーとC ++ファイルストリームオブジェクトには違いがあります。exit()-Cライブラリの一部である-前者と調整してフラッシュするように設計されていますが、後者をバイパスできます:標準C ++ fstreamコンテンツでもディスクにフラッシュされません(試してください-私はそうしましたが、Linux /で失敗します) GCC)、そして明らかにバッファリングされたI / Oを持つユーザー定義型もフラッシュすることは期待できません。
Tony Delroy、2010年

9
注:ステートメント:ローカルスコープのオブジェクトに対してデストラクタは呼び出されません!C ++ 11には当てはまりません:-スレッドストレージ期間を持つ現在のスレッドに関連付けられたオブジェクトは破棄されます(C ++ 11のみ)。cplusplus.com/reference/cstdlib/exit
Ilendir

7
つまり、thread_localオブジェクトのデストラクタが呼び出されます。他のローカルオブジェクトのデストラクタはまだ呼び出されません。ideone.com/Y6Dh3f
HolyBlackCat 14

3
ちなみに、とにかく単刀直入ですが、Cを使用している読者にとってこの回答は依然として混乱を招く可能性があるためです。Cの場合、exit()ファイルを完全に閉じるという問題は実際には間違っています。データがフラッシュされない可能性があるのは、その逆の場合のみです。つまり、returnfrom main()を使用し、呼び出しが行われたsetbuf()場合setvbuf()、またはmain()(以下のRの回答で説明されているように)自動ストレージとして宣言されたバッファーを使用した場合です。この質問にCとC ++の両方のタグが付けられているのは本当に残念です(そしてコーディングスタイル-スタイルの問題ではありません!)。
グレッグA.ウッズ

25

もう1つの違い exitは、標準ライブラリ関数であるため、ヘッダーを含めて標準ライブラリとリンクする必要があることです。(C ++で)説明すると、これは有効なプログラムです。

int main() { return 0; }

ただし、使用exitするにはインクルードが必要です。

#include <stdlib.h>
int main() { exit(EXIT_SUCCESS); }

さらに、これは追加の仮定を追加します:からの呼び出しexitmainはゼロを返すのと同じ副作用があります。他の人が指摘したように、これはどのような実行可能ファイルを作成するか(つまり、誰がを呼び出すかmain)に依存します。Cランタイムを使用するアプリをコーディングしていますか?Mayaプラグイン?Windowsサービス?ドライバー?それぞれのケースで、がとexit同等かどうかを調べるための調査が必要になりますreturn。IMHOを本当に意味するexitときに使用すると、コードがさらに混乱します。OTOH、本当にそういう意味なら、是非使ってください。 return exit


16

そこ好む少なくとも一つの理由は、exitあなたのいずれかの場合:atexitハンドラに自動ストレージ期間のデータを参照main、またはあなたが使用している場合setvbufsetbuf、標準の一つに割り当てることで自動ストレージ期間のバッファをストリームmain、その後から戻って、main生成します未定義の動作exitですが、呼び出しは有効です。

別の潜在的な使用法(ただし、通常はおもちゃのプログラム用に予約されています)は、を再帰的に呼び出してプログラムを終了することですmain


1
@Seb特別なことは何もありませんmain()-それは他のものと同様の関数です。一方、標準には特別な言及があるため、標準は、それがどのように定義されているか、main()およびそれに近いものや大切なものについてかなり注意する必要があります。しかし、結局のところ、標準では、コンパイラはでの自動ストレージに関して特別なことを行う必要はありません(また、そうしてはなりませんmain()。コメントで参照した段落の下の脚注#11をよく読んでください。
グレッグA.ウッズ

1
@ GregA.Woods興味深い。いくつかの有益なテキストに矛盾するいくつかの規範的なテキストがあるようです。ISO / IEC指令によると規範的な参照は「必須」と見なされ、情報は補足的なものと見なされます...さらに、要件を伝える「意志」という言葉の使用は無効です。前述のドキュメント(付録H)によると、要約すると、有益なテキストは間違いなく無効です。
2015

2
@Seb:意図は明らかに自動ストレージの動作に関する要件をオーバーライドすることではなく、脚注は明らかにこれを明確にするために書かれました。はい、C標準には不正確で不適切な表現があります。それを読んだ人なら誰でもこれを知っています。また、意図がすでに明らかであるため、委員会は通常、このような問題を修正しないことも知っています。
R .. GitHub ICE HELPING ICEの停止

1
@Seb:これは、あなたが正しいことを証明するための議論や裁判ではありません。目標、実際のC言語(意図され、実装されている)が何であるかを明確に理解し、読者に役立つ回答でそれを表現することです。規範的なテキストは、本質的に脚注で修正されている方法で、(それが表現することになっているものの意図に反して)微妙に間違っています。これに満足できない場合は、欠陥レポートを送信してください。ただし、返信は期待しないでください。これがWG14の転がり方です...
R .. GitHub ICE

3
@Seb:C言語は、標準の自然言語のテキストを完全に厳密であるかのように解釈することで理解できると信じているようです。これは不可能です。仕様には誤りが含まれており、WG14は、簡単な脚注で間違いを既に知っていて、読者がそれを理解できることを明確にしても、時間の無駄を省いています。
R .. GitHub ICEのヘルプの停止2015

5

returnの標準プロトタイプmain()はを返すと言っているので、私は常に使用していintます。

mainとはいえ、一部のバージョンの標準は特別な扱いをしており、明示的なreturnステートメントがない場合は0を返すと想定しています。次のコードがあるとします:

int foo() {}
int main(int argc, char *argv[]) {}

G ++は警告を生成するだけでfoo()、不足しているからの戻り値を無視しmainます:

% g++ -Wall -c foo.cc
foo.cc: In function int foo()’:
foo.cc:1: warning: control reaches end of non-void function

私は、0を返すと仮定だ、あなたがメインで値を返さない場合は、そのCについて知っているが、C ++標準を指定しません
ジェイソン・ベイカー

C99は同じであるかのように思える: faq.cprogramming.com/cgi-bin/...
ジェイソン・ベイカー

2
C99およびC ++は、returnステートメントがない場合は0を返し、C90はそうしません。
d0k 2009年

関数が戻り値を持つものとして宣言されているreturnからといって、その実行を終了するために使用する必要があるわけではありません。呼び出しexit()は、関数の実行を終了するための有効な方法であり、必要な場合もあります。実際、私や他の人が他の場所で説明したように、exit()から呼び出しても、main()プロセス全体を終了するというはるかに明確な意図が伝えられ、プロセスが終了するまで自動ストレージが保持され、コードの将来のリファクタリング中のメンテナンスが容易になります。したがって、C がプロセスを終了することを意図してreturnin を使用main()することは、間違いなく悪い習慣です。
グレッグA.ウッズ

@ GregA.Woodsそれはあなたの意見ですが、反対票に値することはほとんどありません!上で書いたものは標準完全に一致していますが、あなたの主張は単なる意味論です。
Alnitak 2016年

5

プログラムが実際に終了する前に自動ストレージが再利用されるのを回避するために、exit()の使用についてR.のコメントを強く示しますmain()。のreturn X;ステートメントは、main()への呼び出しと正確に同じではありません。戻り時にexit(X);動的なストレージがmain()消失するmain()ためexit()ですが、代わりにへの呼び出しが行われた場合には消失しません。

さらに、CまたはCのような言語では、returnステートメントは、実行が呼び出し元の関数で継続することを読者に強く示唆しています。この実行の継続は、通常、main()関数を呼び出したCスタートアップルーチンを数えると技術的に当てはまりますが、そうではありません。プロセスを終了するつもりであるときに正確に何意味するか。

結局のところ、あなたは除いて、他の機能の中から、あなたのプログラムを終了したい場合はmain()、あなたがしなければならない電話をexit()main()同様に一貫してそうすることで、コードがはるかに読みやすくなり、誰でもコードをリファクタリングするのがはるかに簡単になります。つまりmain()、他の関数からにコピーされたコードは、呼び出しでreturnあるはずの偶発的なステートメントが原因で誤動作することはありませんexit()

したがって、これらのすべてのポイントを組み合わせると、少なくともCでは、ステートメントを使用してでプログラムを終了するのは悪い習慣であるという結論returnになりmain()ます。


C標準の5.1.2.2.3p1が興味深いかもしれません...
自閉症の

この回答は、状況に応じて回答で示されているように、Cプログラムでは慎重に検討する価値があります。C ++で使用するには、前述のすべての警告に対して慎重に検討する必要があります。C ++の場合exit()、一般的には避けることをお勧めしますが、特定の状況でa throwまたはabort()代替案が機能しない場合は使用してください。ただし、特にexit()mainでの使用は避け、一般的な方法として、代わりにmainでreturnを使用してください。
Eljay

5

exit()は 'return'が行わない特別なことをしますか?

まれなプラットフォーム用の一部のコンパイラーでは、exit()その引数をプログラムの終了値に変換する場合がありますが、returnからmain()は、変換せずに値を直接ホスト環境に渡す場合があります。

標準では、これらのケースで同一の動作が必要です(具体的には、int互換性のあるものを返すことは、その値でmain()呼び出すことと同等である必要があるexit()と述べています)。問題は、OSごとに終了値を解釈するための規則が異なることです。多くの(多くの)システムでは、0は成功を意味し、それ以外は失敗を意味します。しかし、たとえばVMSでは、奇数値は成功を意味し、偶数値は失敗を意味します。から0を返した場合main()、VMSユーザーはアクセス違反について厄介なメッセージを表示します。実際にはアクセス違反はありませんでした。これは単にエラーコード0に関連付けられた標準メッセージです。

その後、ANSIは、一緒に来て、祝福されたEXIT_SUCCESSEXIT_FAILURE引数として、あなたはに渡すことができますexit()。また、標準では、exit(0)はと同じように動作する必要があるためexit(EXIT_SUCCESS)、ほとんどの実装はに定義さEXIT_SUCCESSれてい0ます。

したがって、この標準では、たまたま値0のエラーコードを返す標準的な方法がないため、VMSでバインドされます。

したがって、1990年代初頭のVAX / VMS Cコンパイラは、からの戻り値を解釈せずmain()、単にホスト環境に任意の値を返しました。しかし、それを使用exit()した場合、標準が要求することを実行します。つまり、成功コードと一般的な失敗コードに変換EXIT_SUCCESS(または0)しEXIT_FAILUREます。使用するためにはEXIT_SUCCESS、あなたが持っていたためにそれを渡すためにexit()、あなたからそれを戻すことができませんでしたmain()。そのコンパイラのより新しいバージョンがその動作を維持していたかどうかはわかりません。

以前は次のように見える移植可能なCプログラム:

#include <stdio.h>
#include <stdlib.h>

int main() {
  printf("Hello, World!\n");
  exit(EXIT_SUCCESS);  /* to get good return value to OS */
  /*NOTREACHED*/ /* to silence lint warning */
  return 0;  /* to silence compiler warning */
}

余談ですが、私が正しく思い出せば、終了値のVMS規則は奇数/偶数よりも微妙です。実際には、下位3ビットのようなものを使用して、重大度レベルをエンコードします。ただし、一般的に言って、奇妙な重大度レベルは成功またはその他の情報を示し、偶数のものはエラーを示しました。


いくつかの古い事前ANSIコンパイラは値を治療している可能性があるreturnedことにより、main異なったに渡された値からexitの戻り値の型ならば」が、標準では、具体的言う- main機能と互換性タイプであるint、の最初の呼び出しからの戻りmain関数でありますexit関数から返された値をmain引数として関数を呼び出すのと同じです。」それがC11です。C89 / C90の表現はほぼ同じでした。
キース・トンプソン

確かに。それでも、一部のANSI時代のコンパイラはこれを正しく行わず、ホスト環境に正しい戻り値を返すために、exitを明示的に使用する必要がありました。標準では(それでも)0をと同じように扱う必要EXIT_SUCCESSがあるため、値が0のプラットフォーム固有の障害ステータスを返す方法はありませんでした。このため、時代の一部のコンパイラはreturn-from-mainを扱いました。そしてexit()違う。
エイドリアンマッカーシー

そのための引用はありますか?別の問題は、現在のコンパイラにその特定のバグがあるかどうかです。あなたの答えは現在形で表現されたものです。
Keith Thompson

それは公正な批判です。私が知っている特定のケースに範囲を限定するために文言を変更しました。
Adrian McCarthy

0

Cでは、からの戻りmainexit、同じ値での呼び出しとまったく同じです。

セクション5.1.2.2.3 C標準の状態:

メイン関数の戻り値の型がintと互換性のある型である場合、メイン関数への最初の呼び出しからの戻りは、メイン関数から返された値を引数として出口関数を呼び出すことと同じです。11)メイン関数を終了する}に到達すると、値0が返されます。戻り値の型がintと互換性がない場合、ホスト環境に返される終了ステータスは指定されていません。

他の回答で述べたように、C ++のルールは少し異なります。


-1

実際には違いがあるexit(0)return(0)main、あなた- main関数が複数回呼び出されるが。

次のプログラム

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv) {
  if (argc == 0)
    return(0);
  printf("%d", main(argc - 1, argv));
}

として実行

./program 0 0 0 0

次の出力になります:

00000

しかしこれは:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv) {
  if (argc == 0)
    exit(0);
  printf("%d", main(argc - 1, argv));
}

引数に関係なく何も出力しません。

だれもmain明示的にあなたを呼び出さないことが確実である場合、それは一般的に技術的に大きな違いではありませんが、より明確なコードを維持exitすることははるかに良く見えます。何らかの理由で電話をかけたいmain場合は、ニーズに合わせて調整してください。

Cについて話す

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