C言語のmain関数と実行時引数の深層解説

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が最後に実行

タグ: C言語 main関数 コマンドライン引数 GCC コンストラクタ属性

7月1日 23:32 投稿