C言語のきほん|コマンドライン引数の活用

コマンドライン引数を使いこなして、実行するたびに動きを変えられるCプログラムを書けるようになろう

C言語のプログラムは、ソースコードの中に値を書いて動かすだけでなく、実行するときに外からデータを渡して動作を変えることもできます。
その代表的な仕組みが、コマンドライン引数です。

コマンドライン引数を使うと、プログラムを実行するたびに違う数値や文字列を渡せるようになります。
たとえば、計算に使う数値を指定したり、表示する文字列を変えたり、ファイル名やオプションを外から与えたりできます。つまり、同じプログラムでも、実行時の入力によって役割を変えられるようになるわけです。

これまで学んできた main 関数は、引数を持たない形で書くことが多かったかもしれません。
しかし実際には、main 関数は argc と argv という引数を受け取ることができ、そこからコマンドライン引数を利用できます。

この仕組みを理解すると、プログラムはぐっと実用的になります。
ただし、コマンドライン引数は最初から数値として渡されるわけではなく、文字列として受け取られる点に注意が必要です。そのため、数値として使いたいときは変換が必要になりますし、引数の数が足りているかどうかを argc で確認することも大切です。

ここでは、受け取ったコマンドライン引数をどのように活用するのかを、サンプルプログラム、表、図のイメージを使いながら、やさしく丁寧に見ていきます。
さらに、実践問題や応用的なオプション解析の例を通して、実際に使える形へつなげていきましょう。

コマンドライン引数を活用すると何が便利なのか

コマンドライン引数を活用すると、プログラムの動きを実行時に変えられるようになります。

たとえば、次のようなことができます。

活用例渡す内容
2つの数値を受け取って計算する数値
複数の単語の文字数を調べる文字列
判定対象の整数を外から与える数値
オプションで動作を切り替える-n や -s のような指定

ソースコードの中に値を固定で書いてしまうと、別の入力で試すたびにプログラムを書き換える必要があります。
でも、コマンドライン引数を使えば、プログラム自体はそのままで、実行のたびに別の値を渡せます。

この柔軟さが、コマンドライン引数の大きな魅力です。

受け取った引数は文字列であることを忘れない

ここでとても大事なのが、コマンドライン引数はすべて文字列として渡されるという点です。

たとえば、次のように実行したとします。

sample 120 30

見た目は数字ですが、main 関数に渡されるときには 120 も 30 も文字列です。
そのため、足し算をしたいなら、そのままでは使えません。

このとき必要になるのが、文字列から数値への変換です。

受け取ったもの実際の型数値として使うには
120文字列変換が必要
30文字列変換が必要

整数に変換するなら atoi や strtol、浮動小数点数に変換するなら atof などが使われます。
今回の基本例では、まず atoi を使ったシンプルな活用から確認していきます。

2つの整数を受け取って計算する例

2つの整数を受け取って差を求めるシンプルなプログラムです。

ファイル名:14_7_1.c

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    if (argc != 3) {
        printf("使い方: %s 整数1 整数2\n", argv[0]);
        return 1;
    }

    int num1 = atoi(argv[1]);
    int num2 = atoi(argv[2]);

    printf("%d - %d = %d\n", num1, num2, num1 - num2);

    return 0;
}

実行方法例

sample 50 18

実行結果例

50 - 18 = 32

引数を指定しない場合

sample

実行結果例

使い方: sample 整数1 整数2

このプログラムでは、argv[1] と argv[2] に入っている文字列を atoi で整数に変換し、その差を計算しています。
とてもシンプルですが、コマンドライン引数の活用として大事な要素がしっかり入っています。

このプログラムで大切なポイント

この例では、次の3つが特に大切です。

argc で引数の個数を確認する

引数が足りないのに argv[1] や argv[2] を使おうとすると、正しくない領域を読んでしまう危険があります。
そのため、先に argc を確認することが大切です。

今回の例では、必要なのは次の3つです。

  • argv[0] のプログラム名
  • argv[1] の1つ目の整数
  • argv[2] の2つ目の整数

つまり argc は 3 でなければなりません。

atoi で文字列を整数に変換する

argv に入っているのは文字列です。
そのため、計算する前に atoi で int 型へ変換しています。

使い方を表示すると親切

引数が不足しているときに、ただエラーにするのではなく、どのように実行すればよいのかを表示してあげると、使う人にとってとても親切です。
こうした使い方メッセージは、コマンドラインプログラムではよく書かれます。

図で見るコマンドライン引数の活用

この図では、コマンドラインに入力した 50 と 18 が、そのまま整数として渡されるのではなく、まず argv の中では文字列として扱われていることを表しています。
そして、それを atoi によって整数へ変換してから計算している流れが見えるようになっています。

この考え方がつかめると、コマンドライン引数を扱うときに、なぜ変換関数が必要なのかが自然に理解しやすくなります。

atoi を使うときの注意

atoi は手軽に使えて便利ですが、厳密なエラー検出が苦手です。
たとえば、数字ではない文字列が混ざっていても、期待通りに判定しにくいことがあります。

そのため、簡単なサンプルや学習の初期段階では便利ですが、よりしっかり入力を検証したい場合は strtol のような関数を使うほうが安全です。

比較すると、次のようになります。

関数特徴
atoi手軽で簡単
strtolエラー検出がしやすい

学習の流れとしては、まず atoi で仕組みを理解し、次に strtol でより丁寧な入力チェックへ進むとわかりやすいです。

コマンドライン引数は数値以外にも使える

コマンドライン引数は、もちろん数値だけに使うものではありません。
文字列もそのまま受け取れるので、たとえば次のような使い方もできます。

  • 名前を受け取って表示する
  • 複数の単語の文字数を調べる
  • オプション文字列で処理を分ける
  • ファイル名やモードを指定する

つまり、コマンドライン引数は「実行時の入力窓口」として使えるわけです。

argc と argv という名前について

main 関数でよく使われる argc と argv という名前は、それぞれ次の略です。

名前意味
argcargument count
argvargument vector

日本語にすると、argc は引数の個数、argv は引数の配列という意味です。

実際には別の変数名を付けることもできますが、C言語では argc と argv が非常によく使われています。
そのため、特別な理由がない限り、この名前のまま使うほうがコードの可読性は高くなります。

実践問題

コマンドライン引数で1つ以上の整数値を受け取り、その合計を求めるプログラムを作成してください。
ただし、コマンドライン引数の数が2未満の場合にはエラーメッセージを表示し、計算を行わないでください。

実行方法と結果例

sample 10 20 30 40

合計 = 100

解答例

ファイル名:14_7_2.c

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    if (argc < 2) {
        printf("整数値を1つ以上指定してください。\n");
        return 1;
    }

    int sum = 0;

    for (int i = 1; i < argc; i++) {
        sum += atoi(argv[i]);
    }

    printf("合計 = %d\n", sum);

    return 0;
}

解説

この問題では、argv[1] から argv[argc - 1] までを順番に整数へ変換し、sum に加算しています。
ここで大切なのは、argv[0] はプログラム名なので計算対象に含めないことです。

また、引数が1つも与えられていない場合は argc が 1 になるため、argc < 2 でチェックしています。
このように、コマンドライン引数を複数まとめて処理するときは、for 文と argc の組み合わせがとても便利です。

もう少し応用的な活用

コマンドライン引数は、ただ順番に受け取るだけでなく、オプション形式で扱うこともできます。
たとえば、-n で回数、-s で文字列、というように、引数の意味を明確にする方法です。

この形式を使うと、次のような実行ができます。

sample -n 3 -s hello

あるいは、

sample -s hello -n 3

このように順番が変わっても正しく処理できるように作ると、より実用的なプログラムになります。

strtol を使う理由

オプション付きの引数解析では、数値変換の失敗をきちんと見つけたい場面が増えます。
そのため、このような場面では atoi より strtol のほうが向いています。

strtol は、変換できなかった文字が残っていないかを endptr で確認できるので、入力ミスを見つけやすいです。

たとえば abc を整数に変換しようとした場合、atoi では扱いがあいまいになりやすいですが、strtol ならより丁寧にエラー判定ができます。

実践問題

次の仕様に従ってプログラムを作成してください。

  • コマンドライン引数として -c 数値 と -w 文字列 を受け取る
  • 文字列 w を c 回表示する
  • 引数の順序は固定しない
  • 数値変換には strtol を使う
  • オプション不足や変換失敗時にはエラーメッセージを表示する

実行方法と結果例

sample -c 4 -w Go

Go Go Go Go

解答例

ファイル名:14_7_3.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main(int argc, char *argv[])
{
    char *word = NULL;
    int count = -1;

    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-c") == 0) {
            if (i + 1 >= argc) {
                printf("エラー: -c の後に数値が必要です。\n");
                return 1;
            }

            char *endptr;
            errno = 0;
            long value = strtol(argv[i + 1], &endptr, 10);

            if (errno != 0 || *endptr != '\0') {
                printf("エラー: \"%s\" は正しい整数値ではありません。\n", argv[i + 1]);
                return 1;
            }

            count = (int)value;
            i++;
        }
        else if (strcmp(argv[i], "-w") == 0) {
            if (i + 1 >= argc) {
                printf("エラー: -w の後に文字列が必要です。\n");
                return 1;
            }

            word = argv[i + 1];
            i++;
        }
    }

    if (count < 0 || word == NULL) {
        printf("使い方: %s -c 数値 -w 文字列\n", argv[0]);
        return 1;
    }

    for (int i = 0; i < count; i++) {
        printf("%s", word);
        if (i < count - 1) {
            printf(" ");
        }
    }
    printf("\n");

    return 0;
}

簡易解説

この問題では、argv を先頭から順番に見ながら、-c と -w を探しています。
-c の次には数値、-w の次には文字列が来る想定なので、見つけたらその次の要素を読み取っています。

数値変換には strtol を使っており、errno と endptr を使って変換エラーをチェックしています。
このようにすると、abc のような不正な入力も見つけやすくなります。

また、引数の順序を固定していないので、-c が先でも -w が先でも正しく解析できます。
この考え方は、コマンドラインプログラムを実用的にするうえでとても大切です。

詳細解説

順番に丁寧に解説します。

① strtol の行の解説

long value = strtol(argv[i + 1], &endptr, 10);

引数の意味

引数内容
argv[i + 1]変換対象の文字列(例: "123")
&endptrどこまで変換できたかを受け取る
1010進数として変換

戻り値

内容
value変換された数値

endptr の重要な役割
endptr は「変換が止まった位置」を指します。

例① 正常な入力

"123"

変換後:

[1][2][3][\0]
           ↑
        endptr
→ 最後まで変換成功

例② 途中に文字がある

"123abc"

変換後:

[1][2][3][a][b][c]
          ↑
        endptr
→ "abc" が残っている

例③ 全く変換できない

"abc"

変換後:

[a][b][c]
 ↑
endptr
→ 1文字も数値として読めていない

errno の役割

errno は自分で宣言していなくても使えるのは、

#include <errno.h>

これをインクルードすると、errno が使えるようになります。

errno = 0;

としている理由:

  • strtolの実行前にリセット
  • エラーが起きたかを正確に判定するため

エラー例

入力結果
非常に大きい数errno = ERANGE
非常に小さい数errno = ERANGE

② エラーチェックの行

if (errno != 0 || *endptr != '\0')

この条件は 「正しい整数かどうかの最終判定」 です。

条件① errno != 0

意味
変換中にエラーが発生している

検出できるもの

  • オーバーフロー(大きすぎる数)
  • アンダーフロー(小さすぎる数)

条件② *endptr != '\0'

意味
「文字列の最後まで数値として読み取れていない」

つまり:

  • 余計な文字が残っている
  • 数値として完全ではない

例で整理

入力errnoendptr判定
"123"0'\0'OK
"123abc"0'a'NG
"abc"0'a'NG
巨大な数ERANGE'\0'NG

条件全体の意味

if (errno != 0 || *endptr != '\0')

これは次の意味になります。

「エラーが発生した または 完全な整数ではない」

なぜこの2つを組み合わせるのか

どちらか一方だけでは不十分です。

チェック不足している点
errnoだけ"123abc" を見逃す
endptrだけオーバーフローを見逃す

結論

この2つを組み合わせることで:

  • 数値として正しいか
  • 範囲内に収まっているか

の両方を完全にチェックできる

全体の流れまとめ

文字列を受け取る
        ↓
strtolで数値変換
        ↓
errnoで範囲エラー確認
        ↓
endptrで文字混入確認
        ↓
完全な整数ならOK

重要ポイント

この書き方は実務でもよく使われる「安全な数値変換の定番パターン」です。

errno = 0;
long value = strtol(str, &endptr, 10);

if (errno != 0 || *endptr != '\0') {
    // エラー処理
}

確認問題

次の項目について、正しいものには○、間違っているものには×をつけてください。

① コマンドライン引数は、main 関数では最初から整数型や浮動小数点型として渡される。
② argv[0] には通常、実行されたプログラム名が入る。
③ atoi を使うと、文字列を整数に変換できる。
④ 引数の個数が足りない場合でも、argv[1] や argv[2] をそのまま使って問題ない。
⑤ strtol は、数値変換の失敗をより丁寧に確認しやすい。
⑥ argc には、プログラム名を含めた引数の総数が入る。
⑦ コマンドライン引数が何も指定されなかった場合、argc は 0 になる。
⑧ オプション形式の引数解析では、引数の順序が変わる可能性も考えて実装するとよい。
⑨ atoi は、変換失敗を厳密に判定するのにとても向いている。
⑩ コマンドライン引数を使うと、実行のたびにプログラムの動作を変えやすくなる。

解答と解説

① ×
コマンドライン引数は、すべて文字列として main 関数に渡されます。数値として使いたい場合は、atoi や strtol などで変換が必要です。

② ○
argv[0] には通常、実行したプログラム名が入ります。そのため、実際の入力引数は argv[1] 以降に入ります。

③ ○
atoi は、文字列を int 型の整数値へ変換するための関数です。学習用の基本例ではよく使われます。

④ ×
引数が足りない状態で argv[1] や argv[2] にアクセスするのは危険です。先に argc で個数を確認する必要があります。

⑤ ○
strtol は endptr や errno を使って変換の失敗を確認しやすいため、atoi より丁寧なチェックができます。

⑥ ○
argc は argument count の略で、プログラム名も含めた引数の総数を表します。

⑦ ×
実行ファイル名は argv[0] に入るため、追加の引数がなくても argc は 1 です。

⑧ ○
オプション形式では、利用者が -n と -s の順番を入れ替えて指定することもあるため、順不同でも処理できる実装が便利です。

⑨ ×
atoi は簡単ですが、変換失敗を厳密に判定する用途にはあまり向いていません。そうした場面では strtol がより適しています。

⑩ ○
コマンドライン引数を使うと、実行時に入力を変えるだけで動作を変えられるため、柔軟なプログラムを作りやすくなります。

メモリリーク

メモリリークとは、malloc などで動的に確保したメモリを、使い終わったあとに free で解放しないまま放置してしまうことです。

この状態が続くと、使えるはずのメモリが少しずつ減っていきます。
特に、長時間動き続けるプログラムやサービスでは、こうした未解放の領域が積み重なり、やがてメモリ不足や動作不安定の原因になります。

現代の OS では、プログラム終了時にそのプロセスが使っていたメモリは回収されることが多いです。
ただし、それに頼りきるのではなく、不要になったタイミングで自分で free を呼んで解放する習慣がとても大切です。

メモリリークは、バッファオーバーフローとは別の問題です。
バッファオーバーフローは、確保した領域を超えて書き込むことで起こる不正アクセスです。
一方、メモリリークは「解放し忘れて空き領域を減らしてしまう問題」です。

この違いも、あわせて押さえておくと理解が安定します。

学習のときに意識したいポイント

コマンドライン引数の活用では、次の点を意識すると理解しやすいです。

注目する点内容
argc引数の数を確認する
argv実際の引数文字列を取り出す
文字列から数値への変換atoi や strtol を使う
入力チェック引数不足や変換失敗を確認する

特に大切なのは、「コマンドライン引数は文字列であり、使う前に必要な形へ変換する」という考え方です。
この視点があると、数値計算のプログラムも、オプション解析のプログラムもぐっと理解しやすくなります。