コマンドライン引数
起動するときに値を渡す
ここまでのプログラムは、動き始めてから scanf で値を受け取っていました。ただ、保存先のファイル名のように、動き出す前にもう決まっているものもあります。そういう値は起動時のコマンドラインで渡すのが普通です。
./seiseki data.bin 80 90この data.bin や 80 を受け取る入口が、main の引数です。
argc と argv
main には2つの書き方があります。
int main(void)
int main(int argc, char *argv[])argc は渡された語の 個数、argv はその語を文字列として並べた配列です。さきほどの起動なら、argc は 4、argv[0] は実行ファイル名、argv[1] は "data.bin"、argv[2] は "80"、argv[3] は "90" になります。
ここで必ず一度つまずくのが argv[0] です。自分で指定したのは3つなのに argc が 4 になるのは、実行ファイル名が先頭に入るからです。そしてその中身は、どう起動したかで ./seiseki にも /usr/local/bin/seiseki にも変わります。中身に頼った処理を書いてはいけません。
char *argv[] は「char へのポインタの配列」で、前回の int ** と同じ形です。1本1本が文字列の先頭を指しています。argv[argc] は NULL だと規格で決まっているので、個数を使わずに終端まで歩くこともできます。
届くのは必ず文字列
80 と打っても、プログラムに届くのは数値ではなく '8' '0' と終端文字の並びです。計算に使うなら変換が要ります。
int score = atoi(argv[2]);atoi は変換できなかったときも 0 を返すだけなので、失敗したのか本当に 0 だったのかを区別できません。厳しく見たいときは strtol を使い、どこまで読めたかを受け取って確かめます。
順番で決めるか、名前で決めるか
引数の意味を位置だけで決めると、./seiseki 80 data.bin のように並べ替えられただけで壊れます。実用的なコマンドが -o data.bin や --out data.bin のような書き方を用意しているのは、位置ではなく名前で意味を決めるためです。仕組みとしては、argv を先頭から見ていって -o を見つけたら次の1本を保存先として扱う、というだけの走査です。ここではまず位置で決める形に慣れます。
個数の確認を先に書く
argv を読む前に、必要な数がそろっているかを必ず確かめます。argc を見ずに argv[1] を読むと、何も渡されなかったときに配列の外を触ることになり、その場で落ちます。落ちたプログラムは何も説明してくれないので、足りないときは使い方を案内して終わるのが親切です。
では、保存先の名前と点数を引数で受け取るプログラムを書いてみましょう。
要件
- argc が 3 未満のときは 引数が足りません と 保存先と点数を1つ以上指定してください の2行だけを出して終わる
- そろっているときは argcは4です の形で argc をそのまま出す
- 続けて 保存先はkokugo.txtです の形で argv[1] を出す
- 最後に 3科目の合計は240点です の形で、点数の個数と合計を出す
- 実行ファイル名 argv[0] は表示しない
入出力例
main("") → "argcは5です
保存先はkokugo.txtです
3科目の合計は240点です"
main("") → "argcは4です
保存先はsugaku.txtです
2科目の合計は155点です"
main("") → "引数が足りません
保存先と点数を1つ以上指定してください"