return n
(main
関数の)とexit(n)
Cの間に違いはありますか?CまたはPOSIX標準で定義されていますか、それともOSまたはコンパイラに依存していますか?
return n
(main
関数の)とexit(n)
Cの間に違いはありますか?CまたはPOSIX標準で定義されていますか、それともOSまたはコンパイラに依存していますか?
回答:
ほとんどの場合、そこには違いませんが、ここでは、使用しているかどうかに応じて、異なる動作をする可能性がありますCのプログラムですreturn 0;
かexit(0);
。
#include <stdio.h>
#include <stdlib.h>
static char *message;
void cleanup(void) {
printf("message = \"%s\"\n", message);
}
int main(void) {
char local_message[] = "hello, world";
message = local_message;
atexit(cleanup);
#ifdef USE_EXIT
puts("exit(0);");
exit(0);
#else
puts("return 0;");
return 0;
#endif
}
以下のためatexit()
のいずれか、コールexit(0);
またはreturn 0;
原因となるcleanup
関数が呼び出されます。違いは、プログラムがを呼び出すとexit(0);
、「呼び出し」main()
がまだアクティブな間にクリーンアップが行われるため、local_message
オブジェクトがまだ存在することです。実行return 0;
、しかし、すぐに呼び出しを終了main()
し、その後、起動するcleanup()
機能を。以来cleanup()
(グローバル介し意味message
に局所的に割り当てられていないのオブジェクトへのポインタ)main
、そのオブジェクトがもはや存在し、動作が未定義です。
これが私のシステムでの動作です。
$ gcc -DUSE_EXIT c.c -o c && ./c
exit(0);
message = "hello, world"
$ gcc c.c -o c && ./c
return 0;
message = ""
$
を使用せずにプログラムを実行する-DUSE_EXIT
と、クラッシュや印刷"hello, world"
(使用されているメモリlocal_message
が破損しない場合)など、何もできません。
ただし、実際には、この違いは、内部でローカルに定義されたオブジェクトmain()
がmain()
、それらへのポインターを保存することによって外部で可視化される場合にのみ現れます。これはもっともらしいことですargv
。(私のシステムでの実験では、によってポイントされたオブジェクトとオブジェクトによってポイントされたオブジェクトが、から戻った後も存在argv
し*argv
続けることを示していますmain()
が、それに依存するべきではありません。)
C
の場合標準では、最初の呼び出しからmainへの戻りは、exitの呼び出しと同等であると述べています。ただし、クリーンアップ中にメインのローカルデータが必要になる場合、メインからの戻りが機能するとは期待できません。
C ++の場合
exit(0)を使用してプログラムを終了する場合、ローカルスコープの非静的オブジェクトのデストラクタは呼び出されません。ただし、戻り値0を使用すると、デストラクタが呼び出されます。
プログラム1 –-exit(0)を使用して終了します
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
class Test {
public:
Test() {
printf("Inside Test's Constructor\n");
}
~Test(){
printf("Inside Test's Destructor");
getchar();
}
};
int main() {
Test t1;
// using exit(0) to exit from main
exit(0);
}
出力:内部テストのコンストラクター
プログラム2 – return 0を使用して終了
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
class Test {
public:
Test() {
printf("Inside Test's Constructor\n");
}
~Test(){
printf("Inside Test's Destructor");
}
};
int main() {
Test t1;
// using return 0 to exit from main
return 0;
}
出力:Inside Test's Constructor
Inside Test's Destructor
たとえば、デストラクタにファイルを閉じるなどのリソースを解放するコードがある場合、デストラクタの呼び出しが重要になることがあります。
静的オブジェクトは、exit()を呼び出してもクリーンアップされることに注意してください。たとえば、次のプログラムを参照してください。
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
class Test {
public:
Test() {
printf("Inside Test's Constructor\n");
}
~Test(){
printf("Inside Test's Destructor");
getchar();
}
};
int main() {
static Test t1; // Note that t1 is static
exit(0);
}
出力:Inside Test's Constructor
Inside Test's Destructor
finally
C標準(C99)は、2つのタイプの実行環境、独立型環境とホスト型環境を定義していることに注意してください。フリースタンディング環境は、CライブラリをサポートしないC環境であり、組み込みアプリケーションなどを対象としています。CライブラリをサポートするAC環境は、ホスト環境と呼ばれます。
C99によると、自立環境ではプログラムの終了は実装で定義されます。そのため、実装定義されている場合main
、return n
およびexit
、彼らの行動は、としてその実装で定義されています。
C99は、ホスト環境の動作を次のように定義しています。
メイン関数の戻りの型がそれと互換性のある型である場合、メイン関数への最初の呼び出しからの戻りは、引数としてメイン関数によって返された値を使用して出口関数を呼び出すことと同等です。メイン関数を終了する}に到達すると、値0が返されます。戻りの型がintと互換性がない場合、ホスト環境に返される終了ステータスは指定されていません。
C標準の観点から、return
ステートメントではexit()
なく、関数であること以外は、実際にはそうではありません。どちらを使用しても、登録されatexit()
ている関数が呼び出され、プログラムが終了します。
注意が必要な状況がいくつかあります。
main()
。実際にはめったに見られませんが、Cでは合法です(C ++では明示的に禁止しています)。main()
。場合によっては、既存のmain()
ものは別の名前に変更され、新しいによって呼び出されmain()
ます。を使用するとexit()
、コードの記述後にこれらのいずれかが発生した場合、特に異常終了しない場合にバグが発生します。それを避けるために、それをmain()
関数として扱い、return
終了させたいときに使用する習慣をつけるのは良い考えです。