argc, argv (c言語のメイン関数がとるコマンドライン引数) の勉強のために書いてみました.
僕と同じ初学者の方で, argc, argvとはなんぞやという人のお役に立てばと思います.
環境としては、macでVSコード(Visual Studio Code)中心に使っていて, TerminalかiTermを使うこともあります.
プログラミング自体がはじめての方, 「コマンドライン, 引数って何?」という方はよかったらこちらをご覧ください.
1. argc, argvとは
1-1. [argc]
"argument count"の略で, 入力したコマンドライン引数の個数をコンピューターが自動的に数えてくれたものです.
プログラム実行コマンド(デフォルトでは"./a.out")もコマンドライン引数としてカウントされます.
% ./a.out
プログラム実行時, コマンドラインにプログラム実行コマンド("./a.out")以外の引数を入力しなければ, 引数は実行コマンドのみになるため, 引数の個数は1つだけとなります. そのため, 上のコマンドラインから計測されるargcの値は1になります.
ただし, カウントされるのはプログラム実行コマンドとその後の入力値のみのようです.
例えば以下のようなコマンドを入力した場合, 上の"user % ./a.out"と同じでargcは1となります.
% gcc -Wall -Wextra -Werror command_line_args.c ; time ./a.out
(プログラムのメイン関数が認識するコマンドライン引数は"./a.out"のみ. コマンドライン上の他の引数 ("gcc"から"time"まで) はメイン関数への引数としてはカウントされない)
1-2. [argv]
"argument vector"の略で, 入力したコマンドライン引数の入力値(文字)を指します.
argv[0]は実行したプログラム名(デフォルトでは"./a.out").
プログラムファイル名の後に入力した値(文字)がargv[1]以降のコマンドライン引数として扱われます.
% ./a.out string 12345
↑ この場合, "string" -> argv[1], "12345" -> argv[2] になる. argcは3.
2. コーディング -> プログラム実行
2-1. ファイル作成 (詳細はここをクリック)
% touch args.c
2-2. コーディング
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("argc = %d\n", argc);
printf("argv[0] = %s\n", argv[0]);
printf("argv[1] = %s\n", argv[1]);
return (0);
}
2-3. コンパイル (詳細はここをクリック)
% gcc -Wall -Wextra -Werror args.c
プログラミング自体がはじめて, 「ファイル作成 -> コンパイル -> プログラム実行をまだやったことない!」 という方は, よかったらこちらをご覧ください.
2-4. プログラム実行
% ./a.out
2-5. 実行結果
無事プログラムが実行されると, 以下のように出力されるはずです.
argc = 1
argv[0] = ./a.out
argv[1] = (null)
上記の"% ./a.out"のプログラム実行コマンドラインでは, "./a.out"以外に何も入力してないので, コマンドライン引数は"./a.out"だけとなります.
そのため, コマンドライン引数の個数は1つだけです. ということで, "argc"は1になり, "./a.out"は最初のコマンドライン引数ということで, "argv[0]"となります.
今回のコードでは"argv[1]"もprintf()関数で出力するようにしてますが, 上のコマンドラインでは”./a.out”の後に何も入力されてません.
そのためargv[1]は入力なしということで, "(null)"(ナル: 無効)と出力されてます.
3. コマンドライン引数を追加
3-1. argv[1] = "string"
では, 今度はコマンドライン引数を追加で入力してみましょう.
文字列を意味する"string"を"./a.out"の後ろに半角スペースを置いたあとに入力してみます.
コマンドライン引数はそれぞれ半角スペースで区切られていることで認識されます.
% ./a.out string
最初に載せたコードを使ってコンパイルすると, 結果は以下のようになるはずです.
argc = 2
argv[0] = ./a.out
argv[1] = string
今回のコマンドライン引数は"./a.out"と"string"ですから, コマンドライン引数は2つ. そのため, "argc"は2となり, "argv[0]"はさっきと同じ "./a.out". さっきは入力なしで, printf()関数では"(null)"として出力されていた"argv[1]"の値は, 今度は"string"となって出力されました.
3-2. argv[2] = "12345"
ではこんなのはどうでしょうか
% ./a.out string 12345
今度は "./a.out" (スペース) "string" (スペース) "12345"という入力内容です. 結果は以下のようになるはずです.
argc = 3
argv[0] = ./a.out
argv[1] = string
3つめのコマンドライン引数"12345"は"argv[2]"として認識されているのですが, "argv[2] = 12345"のように出力されてはいません. これは, 最初に載せたコード内でprintf()関数が出力するように指定したargv[]が, argv[1]までだからです. ただ, 出力はされてないですが、認識はされてるので、"argc"の値は3となってます.
"argv[1]"以降のコマンドライン引数をプリントしたい場合,
printf("argv[1] = %s\n", argv[1]);
の関数をコピペして, "argv[1]"のところの数字を2, 3と変えてあげれば, 他のコマンドライン引数も出力できます.
4. スペースも引数の一部として入力したい
また、コマンドライン引数はスペースで区切られると上に書きましたが, スペースも文字列の一部として入力したい場合はスペースを含んだ文字列を" " (ダブルクォーテーション)で囲んであげましょう.
先ほどの
% ./a.out string 12345
を
% ./a.out "string 12345"
としてみてください. 結果は以下のようになるはずです.
argc = 2
argv[0] = ./a.out
argv[1] = string 12345
5. 安全なコーディングのために
また, コメントで指摘をいただいたので, より安全にプログラムを実行するために下記のようなコードを書いてみました.
#include <stdio.h>
int main(int argc, char *argv[])
{
int i;
i = 0;
printf("argc = %d\n", argc);
while (i < argc)
{
printf("argv[%d] = %s\n", i, argv[i]);
i++;//または i += 1;
}
return (0);
}
こうすると, 入力されたコマンドライン引数をすべて出力しつつ, 引数の数を超えてprintf()関数がargvを出力しようとするのを防げます.
先ほどの
% ./a.out string 12345
をコンパイル後に実行すると, 結果は以下のようになるはずです.
argc = 3
argv[0] = ./a.out
argv[1] = string
argv[2] = 12345