コマンドライン引数

main関数の引数

今までのプログラムでは、main関数の引数はすべて空(void)でした。
実はmain関数にも引数を指定することができます。


#include <stdio.h>

int main(int argc, char *argv[])
{
    printf("引数の数: %d\n", argc);

    for (int i = 0; i < argc; i++)
    {
        printf("%d: %s\n", i, argv[i]);
    }
    getchar();
}

main関数の引数を見てください。
int型の引数と、char*型の配列引数を受け取るようになっています。

main関数はプログラムの実行時に自動的に呼び出されるので、コード中でmain関数に引数を指定して呼び出すことはありません。
main関数の引数はプログラムの実行時にOS(Windowsとか)から渡されます。
これをコマンドライン引数といいます。

コマンドライン引数

例えばWindows上で何かのプログラムを起動する場合、普通はプログラムのアイコン(実行ファイル)を選択してEnterキー、またはマウスでダブルクリックして起動します。
この場合はほとんど意識されないのですが、プログラムによっては起動オプションというものがあります。

例えばメモ帳は、そのまま起動すれば中身が空の状態で起動します。
しかし、メモ帳のアイコンにテキストファイルをドラッグ&ドロップして開けば、そのテキストが開かれた状態で起動します。
メモ帳の通常起動
メモ帳のファイル指定起動

プログラム(実行ファイル)にファイルをドラッグ&ドロップすると、そのプログラムにドロップしたファイルのパスが渡されます。
パスを受け取ったプログラムは、そのファイルを自動で開くなどの処理を行います。

最近はあまり使用されませんが、コマンドプロンプトからプログラムを起動する場合には起動オプションはよく用いられていました。
コマンドプロンプトから起動オプションを指定して起動

「コマンドプロンプト」はWindowsでの呼び方です。
UNIX系OSでは「ターミナル」や「シェル」と呼ばれます。
「コンソール(画面)」とも呼ばれます。

プログラム名に続いて半角スペースを空け、ファイル名を指定するとそのファイルを読み込んだ状態でメモ帳が起動されます。
これはファイルをドロップするのと同じことです。

ただし、どのような値を受け取れるかはプログラムによって異なります。
メモ帳は、受け取ったファイルパスを自動的に開いた状態で起動するようにプログラムが作られているということになります。

長くなりましたが、外部から値を指定してプログラムを実行した時、その値を受け取る方法がコマンドライン引数であり、コマンドライン引数はmain関数の引数として受け取れるわけです。

コマンドプロンプトやターミナルでは半角スペースがコマンドの区切りとして使用されます。
引数はいくつでも指定することができます。


$ ./sample.exe -a -b -cdefg

上の例では「sample.exe」という実行ファイルに「-a」「-b」「-cdefg」という文字列を渡しています。
この文字列をどのように解釈して使用するかはそのプログラム次第です。
(先頭の「$」はコマンド入力待ちを示す記号で、実際には入力しません。「%」や「#」の場合もあります。Windowsの場合は「>」記号です。)

なお、ファイル名やそのパス名に半角スペースが含まれる場合、ダブルクォーテーションで括る必要があります。


//「test program.exe」という実行ファイルに
//「-a」を渡して起動する場合
$ "./test program.exe" -a

//これは「test」という実行ファイルに
//「program.exe」「-a」を
//渡して起動するという意味になる
$ ./test program.exe -a

int argc

main関数の第一引数argcは普通のint型変数(引数)です。
これは次の配列argvの要素数が格納されています。
最低でも1以上の値となります。

「arg」というのは「arguments」の略で、まさに「引数」という意味です。
「argc」のcはcountの略です。

引数名は自由ですが、慣習的に「argc」と「argv」が用いられています。
(argvのvはvectorの略だそうです)

char *argv[]

第二引数の引数argvには、実際に受け取ったコマンドライン引数の文字列が配列で格納されています。
ポインタの配列なので、複数の文字列を同時に受け取れます。

Windowsでは最初の要素には実行ファイル自身のフルパス名が格納されるようです。
そのため、最初のサンプルコードを実行すると実行ファイルの場所が表示されます。
コマンドライン引数のサンプルコードの実行結果

他のOSでも「実行ファイル名」は含まれるようですが、相対パスかもしれませんし、実行時のコマンド(に使用した実行ファイルのパス指定の文字列)がそのまま渡されるかもしれません。

試しに、最初のサンプルコードをビルドして、実行ファイルに適当なファイルをドラッグ&ドロップして起動してみてください。
コードの実行結果に、ドロップしたファイル名が追加されているはずです。
(Visual Studioでビルドした実行ファイルは、プロジェクトファイルの保存場所の「Debug」フォルダ内にあります)
サンプルプログラムにファイルをドラッグ&ドロップした例

少し実用的なコード

以下に、コマンドライン引数を利用して、受け取ったファイルの名前とファイルサイズを表示するコードのサンプルを示します。


#include <stdio.h>
#include <sys/stat.h>

int main(int argc, char *argv[])
{
    if (argc <= 1)
    {
        printf("コマンドライン引数が空です。\n");
        printf("Enterキーで終了");
        getchar();
        return 0;
    }

    struct stat statBuf;
    for (int i = 1; i < argc; i++)
    {
        if (stat(argv[i], &statBuf) == 0)
        {
            printf("%s\n", argv[i]);
            printf("ファイルサイズ: %d\n\n", statBuf.st_size);
        }
        else
        {
            printf("%sは存在しません。\n\n", argv[i]);
        }
    }
    printf("Enterキーで終了");
    getchar();
}

引数argcが1以下ならばコマンドライン引数に何も指定されていないことになるので、プログラムを終了させます。

後はstat関数とstat構造体を利用して、ファイルが存在すればファイルサイズを表示します。
複数のファイルを同時にドラッグ&ドロップしてもすべて表示できます。
(stat関数、stat構造体に関してはファイルサイズの取得を参照)

コマンドライン引数を使用すると外部から自由に値を受け取れるので、例えばfopen関数でファイルを開いてテキストを処理するプログラムにもできます。
受け取れるのはファイル名に限りませんから、特定の文字列を指定して起動した場合には動作を変更するプログラムにすることも可能です。
(起動オプション、起動スイッチ)

なお、ファイルパス以外の文字列をプログラムに渡すには、上述したコマンドプロンプト(ターミナル)を使用します。
Windowsでは、実行ファイルのショートカットを作成し、プロパティで引数を指定する方法があります。
ショートカットファイルでコマンド引数を指定する