main関数の本質
C言語においてプログラムの実行開始点となるmain関数は、OSから直接呼び出される特別な関数です。実装にはいくつかの形式が存在します。
定義形式の多様性
標準規格ではint main(void)またはint main(int, char**)が推奨されますが、実際のコンパイラはより多くの形式を受け入れます。
// 形式1: 引数なし・戻り値なし(非推奨だがコンパイル可能)
void main() {}
// 形式2: 引数なし・戻り値あり(標準的)
int main() { return 0; }
// 形式3: 完全な引数指定
int main(int argument_count, char* argument_vector[]) {
return 0;
}
// 形式4: 古いスタイルの省略記法
main() {}
終了ステータスの活用
main関数の戻り値はプロセスの終了コードとして親プロセスに伝播します。Windowsのバッチ処理では%ERRORLEVEL%で参照可能です。
// success_demo.c
#include <stdio.h>
int main(void) {
puts("成功パターン");
return 0;
}
// error_demo.c
#include <stdio.h>
int main(void) {
puts("エラーパターン");
return 7;
}
コマンドラインでの動作確認:
D:\Projects\ctest> gcc success_demo.c -o succ.exe
D:\Projects\ctest> succ.exe
成功パターン
D:\Projects\ctest> echo %ERRORLEVEL%
0
D:\Projects\ctest> gcc error_demo.c -o err.exe
D:\Projects\ctest> err.exe
エラーパターン
D:\Projects\ctest> echo %ERRORLEVEL%
7
D:\Projects\ctest> succ.exe && err.exe
成功パターン
エラーパターン
D:\Projects\ctest> err.exe && succ.exe
エラーパターン
// 0以外の戻り値で後続コマンドがスキップされる
引数と環境変数の取得
main関数は3つのパラメータで実行コンテキストを受け取れます。第三引数は環境変数へのポインタ配列です。
#include <stdio.h>
int main(int cmd_argc, char* cmd_argv[], char* envp[]) {
int counter = 0;
printf("--- 実行コマンドと引数 ---\n");
while(counter < cmd_argc) {
printf("引数[%d]: %s\n", counter, cmd_argv[counter]);
counter++;
}
// 環境変数一覧(必要に応じて有効化)
counter = 0;
printf("\n--- 環境変数 ---\n");
while(envp[counter] != NULL) {
// printf("%s\n", envp[counter]);
counter++;
}
printf("環境変数総数: %d\n", counter);
return 0;
}
実行例:
E:\Ctest> app.exe
--- 実行コマンドと引数 ---
引数[0]: app.exe
E:\Ctest> app.exe param1 param2
--- 実行コマンドと引数 ---
引数[0]: app.exe
引数[1]: param1
引数[2]: param2
実行順序の制御
GCC拡張の属性指定を使うと、main関数の前後に任意の関数を配置できます。これは静的初期化や後処理に便利です。
#include <stdio.h>
// 非GCCコンパイラ用の互換定義
#if !defined(__GNUC__)
#define __attribute__(x)
#endif
// main関数より前に実行される初期化関数
__attribute__((constructor))
void startup_action(void) {
printf("%sが先に実行\n", __FUNCTION__);
}
// main関数終了後に実行される終了関数
__attribute__((destructor))
void shutdown_action(void) {
printf("%sが最後に実行\n", __FUNCTION__);
}
int main(void) {
printf("%sが本処理を実行\n", __FUNCTION__);
return 0;
}
コンパイルと実行結果:
$ gcc sequence.c -o sequence
$ ./sequence
startup_actionが先に実行
mainが本処理を実行
shutdown_actionが最後に実行