プログラムを実行するたびに、毎回ソースコードを書き換えなくても入力を変えられたら便利だと思いませんか。
C言語では、コマンドラインからプログラムに値を渡すコマンドライン引数という仕組みがあります。
本記事では、その基本から実用サンプル、注意点までを丁寧に解説し、すぐに使える形で身につけられるようにしていきます。
コマンドライン引数とは?C言語の基本を理解しよう
コマンドライン引数の概要と役割

コマンドライン引数とは、プログラムを起動するときに、コマンドライン(ターミナルやコマンドプロンプト)から一緒に渡すことができる情報のことです。
例えば次のようなコマンドを考えます。
myprog hello 123
このときの要素は次のような役割を持っています。
myprog: 実行するプログラム名hello: 1つ目のコマンドライン引数123: 2つ目のコマンドライン引数
C言語では、これらの文字列はmain関数の引数として受け取ることができます。
これにより、プログラムは与えられた引数に応じて処理内容を変えられるようになります。
C言語でコマンドライン引数を使うメリット
コマンドライン引数を使うと、次のようなメリットがあります。
まず、プログラムの汎用性が高くなります。
たとえば、加算プログラムを作る場合、毎回コードの中に固定の数値を書く代わりに、実行時に好きな数値を渡せば、同じプログラムでさまざまな入力に対応できます。
また、設定ファイルのパスや出力ファイルの名前などを外部から指定できるようになるため、ソースコードを書き換えずに動作を切り替えられます。
これにより、同じプログラムを複数の環境や用途で再利用しやすくなります。
さらに、GUIを持たないバッチ処理や自動スクリプトと組み合わせる際にも便利です。
コマンドライン引数を使えば、スクリプトから柔軟にパラメータを渡して自動実行できるようになります。
コマンドライン引数が使われる典型的な場面

コマンドライン引数は、次のような場面でよく使われます。
1つ目はファイル処理プログラムです。
例えば、テキストファイルを読んで加工するプログラムなら、読み込み元ファイル名と書き出し先ファイル名を引数として受け取ることが多いです。
2つ目は数値計算や統計処理です。
計算する数値や、繰り返し回数、アルゴリズムの種類を引数で切り替えられるようにしておくと、テストや比較がやりやすくなります。
3つ目は設定やオプションの切り替えです。
Webサーバやツール類では、ポート番号やログレベル、実行モード(デバッグ・リリースなど)を--portや--modeのようなオプション付き引数で指定するのが一般的です。
C言語のmain関数とコマンドライン引数の書き方
main関数の基本形(int main(void)との違い)
C言語のmain関数は、プログラムの実行が開始される特別な関数です。
最も基本的な形は次のようになります。
#include <stdio.h>
int main(void) {
printf("Hello, world!\n");
return 0; // 正常終了を表す戻り値
}
この形では、main関数はvoidを引数に取り、コマンドライン引数は使いません。
一方、コマンドライン引数を受け取りたい場合、main関数を次のように書きます。
#include <stdio.h>
int main(int argc, char *argv[]) {
// ここで argc, argv を使ってコマンドライン引数を参照します
printf("Program started.\n");
return 0;
}
ここで、int main(void) と int main(int argc, char *argv[]) の主な違いは、後者では引数の個数と内容を受け取るためのパラメータを定義している点です。
戻り値のint型は同じで、終了ステータスを表します。
main関数の引数(argcとargv)の意味

main関数の引数であるargcとargvは、次のような意味を持っています。
argc(argument count)
プログラム名を含めた引数の個数を表す整数です。
例えばmyprog hello 123と実行した場合、argc == 3となります。argv(argument vector)
コマンドライン引数の文字列を格納した配列です。
実際にはchar *型、つまり文字列へのポインタの配列です。
典型的な対応関係は次のようになります。
| 配列要素 | 内容の例 |
|---|---|
| argv[0] | 実行ファイル名(例: myprog) |
| argv[1] | 1つ目の引数(例: hello) |
| argv[2] | 2つ目の引数(例: 123) |
重要なポイントとして、argcは「要素数」であり、有効なargvの添え字は0からargc - 1までであることを必ず意識する必要があります。
argcの使い方と注意点
argcは、与えられた引数の個数を調べるのに使います。
特に、必要なだけの引数が与えられているかどうかのチェックに利用します。
#include <stdio.h>
int main(int argc, char *argv[]) {
// 引数が2つ以上必要だと仮定 (プログラム名 + 1つの引数)
if (argc < 2) {
printf("引数が足りません。\n");
return 1; // エラー終了
}
printf("渡された引数の個数: %d\n", argc);
printf("1つ目の引数は: %s\n", argv[1]);
return 0;
}
渡された引数の個数: 2
1つ目の引数は: hello
注意点として、次のような点があります。
argv[argc]にはアクセスしないことです。配列の範囲外になり、未定義動作を引き起こします。- 引数が足りない場合に
argv[1]などを参照すると必ずバグになります。必ずargcを先にチェックすることが大切です。
argv(文字列配列)の扱い方と型

argvの型はchar *argv[]と書かれることが多いですが、これはchar **argvとほぼ同じ意味です。
つまり、文字列(char配列)へのポインタの配列です。
各argv[i]はchar *型であり、C言語ではヌル終端された文字列として扱えます。
そのため、printfの%sなどで表示できます。
#include <stdio.h>
int main(int argc, char *argv[]) {
int i;
printf("argc = %d\n", argc);
for (i = 0; i < argc; i++) {
// argv[i] は C文字列として扱える
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}
argc = 3
argv[0] = myprog
argv[1] = hello
argv[2] = 123
ポイントとして、argv配列自体やargv[i]が指す領域は、通常はOSが用意した読み取り専用の領域として扱うべきです。
文字列の中身を書き換えること自体は理論上可能な場合もありますが、書き換えないことを前提にコーディングした方が安全です。
WindowsとLinuxでのコマンドライン引数の実行方法

C言語のソースコードは、WindowsでもLinuxでもほぼ同じように書けますが、コンパイル方法や実行方法が少し異なります。
| OS | コンパイル例 | 実行例 |
|---|---|---|
| Linux | gcc main.c -o myprog | ./myprog arg1 arg2 |
| Windows | cl main.c など | myprog.exe arg1 arg2 |
Linuxでは、生成された実行ファイルに対して./myprogのように相対パスを指定して実行することが多いです。
Windowsでは、myprog.exeのように拡張子付きで実行される場合が多いですが、拡張子を省略してmyprogと入力しても実行できる設定になっていることが一般的です。
どちらの場合でも、コマンドライン引数として渡された文字列がargvに格納される仕様は共通です。
したがって、本記事で紹介するプログラムは、基本的にOSを問わず動作します。
コマンドライン引数の実用サンプル集
文字列をそのまま表示するサンプル

まずは最も基本的な、引数として渡された文字列をそのまま表示するサンプルです。
#include <stdio.h>
int main(int argc, char *argv[]) {
// プログラム名 + 1つの引数が必要
if (argc < 2) {
printf("使い方: %s 文字列\n", argv[0]);
return 1;
}
// argv[1] に文字列が入っている
printf("あなたが入力した文字列: %s\n", argv[1]);
return 0;
}
$ ./echoargs hello
あなたが入力した文字列: hello
このプログラムは、引数が足りない場合には簡単な使い方を表示し、1つ以上の引数があれば1つ目の引数(文字列)をそのまま出力します。
数値の合計を計算するサンプル

次に、複数の引数を数値として解釈して合計を求めるサンプルです。
ここでは簡単のためatoiを使いますが、後のセクションでstrtolによる安全な変換についても説明します。
#include <stdio.h>
#include <stdlib.h> // atoi を使うために必要
int main(int argc, char *argv[]) {
int i;
long sum = 0; // 合計は大きくなる可能性を考えて long 型に
// 少なくとも1つの数値引数が必要
if (argc < 2) {
printf("使い方: %s 数値1 数値2 ...\n", argv[0]);
return 1;
}
// argv[1] から argv[argc - 1] までを数値に変換して合計する
for (i = 1; i < argc; i++) {
// 文字列を int に変換 (エラー処理は簡略化)
int value = atoi(argv[i]);
sum += value;
}
printf("合計値: %ld\n", sum);
return 0;
}
$ ./sum 10 20 30
合計値: 60
このプログラムでは、引数を任意個渡すことができ、すべてを足し合わせて合計値を表示します。
ファイル名を引数に受け取るサンプル

ファイル処理では、読み込むファイル名を引数で受け取るのがよくあるパターンです。
ここでは、指定されたテキストファイルの中身をそのまま表示する簡単な例を示します。
#include <stdio.h>
int main(int argc, char *argv[]) {
FILE *fp;
int ch;
if (argc < 2) {
printf("使い方: %s ファイル名\n", argv[0]);
return 1;
}
// 読み込みモードでファイルを開く
fp = fopen(argv[1], "r");
if (fp == NULL) {
// ファイルが開けなかった場合のエラー表示
printf("ファイルを開けませんでした: %s\n", argv[1]);
return 1;
}
// 1文字ずつ読み込んで表示する
while ((ch = fgetc(fp)) != EOF) {
putchar(ch);
}
// ファイルを閉じる
fclose(fp);
return 0;
}
$ ./showfile input.txt
(ここに input.txt の中身がそのまま表示される)
このように、ファイル名をコマンドライン引数で受け取ることで、同じプログラムを様々なファイルに対して使い回すことができます。
オプション付き引数のサンプル

実際のコマンドラインツールでは、オプションと呼ばれるフラグを使って動作モードを切り替えることがよくあります。
ここでは、ごく簡単なオプション解析の例を示します。
#include <stdio.h>
#include <ctype.h> // toupper, tolower を使うために必要
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("使い方: %s -u|-l 文字列\n", argv[0]);
printf(" -u: 大文字に変換\n");
printf(" -l: 小文字に変換\n");
return 1;
}
char *option = argv[1];
char *text = argv[2];
int i;
if (option[0] == '-' && option[1] == 'u') {
// 大文字に変換
for (i = 0; text[i] != '\0'; i++) {
putchar(toupper((unsigned char)text[i]));
}
putchar('\n');
} else if (option[0] == '-' && option[1] == 'l') {
// 小文字に変換
for (i = 0; text[i] != '\0'; i++) {
putchar(tolower((unsigned char)text[i]));
}
putchar('\n');
} else {
printf("不明なオプションです: %s\n", option);
return 1;
}
return 0;
}
$ ./opt -u hello
HELLO
$ ./opt -l HeLLo
hello
ここでは非常に単純な実装ですが、オプションの先頭を'-'で始める流儀は、多くのコマンドラインツールで一般的に使われています。
argcとargvをすべてループで表示するサンプル

コマンドライン引数の動作を理解するには、実際にすべてのargvの中身を表示してみるのが分かりやすいです。
#include <stdio.h>
int main(int argc, char *argv[]) {
int i;
printf("argc = %d\n", argc);
for (i = 0; i < argc; i++) {
printf("argv[%d] = \"%s\"\n", i, argv[i]);
}
return 0;
}
$ ./showargs a b c
argc = 4
argv[0] = "./showargs"
argv[1] = "a"
argv[2] = "b"
argv[3] = "c"
このサンプルを試すことで、argv[0]には実行ファイル名が入り、argv[1]以降に実際の引数が入ることが具体的に理解できます。
エラー処理と使用方法表示のサンプル

実用的なプログラムでは、引数が足りない場合や形式が誤っている場合に、使い方(Usage)を表示するのが一般的です。
次の例は、2つの整数を足し算するプログラムに、簡単なエラー処理とUsage表示を追加したものです。
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
if (argc != 3) {
// 期待する引数の数と違う場合は、使い方を表示して終了
printf("使い方: %s 整数1 整数2\n", argv[0]);
printf(" 例: %s 10 20\n", argv[0]);
return 1;
}
// ここでは簡易的に atoi を使用 (安全性は後述)
int a = atoi(argv[1]);
int b = atoi(argv[2]);
int sum = a + b;
printf("%d + %d = %d\n", a, b, sum);
return 0;
}
$ ./calc
使い方: ./calc 整数1 整数2
例: ./calc 10 20
$ ./calc 10 20
10 + 20 = 30
このように、誤った使い方をされたときに親切なメッセージを表示することで、ユーザビリティの高いプログラムになります。
コマンドライン引数を使う際の注意点とベストプラクティス
argcとargvの境界チェックとエラー回避

コマンドライン引数を扱う際に最も重要なポイントは、境界チェックです。
配列の範囲外アクセスはC言語で頻発するバグの原因になります。
安全に扱うための基本的な考え方は次の通りです。
- 必ず
argcを確認してからargv[i]にアクセスすることです。必要な個数の引数がない場合は、Usageを表示して終了します。 - ループで
argvを走査する場合、ループ条件にi < argcを使うことを徹底し、i <= argcと書かないように注意します。
この基本を守るだけでも、多くのバグを未然に防ぐことができます。
atoiの問題点とstrtolによる安全な変換

文字列を整数に変換する際、atoiは手軽ですが、多くの問題があります。
最も大きな問題は、変換に失敗したかどうかが判別しづらいことです。
例えばatoi("abc")は0を返しますが、"0"を変換した場合も0です。
このため、入力が本当に0なのか、数値でない文字列だったのかを区別できません。
より安全な方法としてstrtolを使うことが推奨されます。
strtolは、変換が行われた位置を示すポインタや、エラーを表すerrnoを利用して、入力が正しい整数かどうかを検証できます。
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("使い方: %s 整数\n", argv[0]);
return 1;
}
char *endptr;
errno = 0; // errno を0で初期化しておく
long value = strtol(argv[1], &endptr, 10); // 10進数として変換
// 1) 変換した結果が範囲外かどうかのチェック
if ((errno == ERANGE && (value == LONG_MAX || value == LONG_MIN))) {
printf("範囲外の数値です: %s\n", argv[1]);
return 1;
}
// 2) 1文字も変換できなかった場合のチェック
if (endptr == argv[1]) {
printf("整数として解釈できません: %s\n", argv[1]);
return 1;
}
// 3) 末尾に余分な文字がないかチェック (例: "123abc")
if (*endptr != '\0') {
printf("末尾に数値以外の文字があります: %s\n", argv[1]);
return 1;
}
printf("正しく変換できました: %ld\n", value);
return 0;
}
$ ./parseint 123
正しく変換できました: 123
$ ./parseint 123abc
末尾に数値以外の文字があります: 123abc
このようにstrtolを使うと、不正な入力を確実に検出できます。
実際のコマンドラインツールでは、atoiではなくstrtolを使うことを強く推奨します。
マルチバイト文字・日本語引数の注意点

日本語などのマルチバイト文字をコマンドライン引数として扱う場合、いくつか注意すべき点があります。
まず、文字コードの違いです。
LinuxではUTF-8が一般的ですが、Windowsの古い環境ではShift_JIS(CP932)が使われていることがあります。
プログラム側で文字コードを意識しないと、文字化けや不正な処理につながることがあります。
次に、マルチバイト文字は1文字が複数バイトになるため、strlenで得られる値は「文字数」ではなく「バイト数」であるという点にも注意が必要です。
argv[i][j]のように1バイト単位で処理すると、文字の境界を壊してしまう可能性があります。
Windowsでは、より厳密に日本語を扱いたい場合、wmainやGetCommandLineWといったワイド文字版APIを利用する方法もあります。
ただし、これはやや高度な話題になるため、まずは「環境によって日本語引数の扱いが変わりうる」ことを理解しておくと良いでしょう。
C言語でのコマンドライン引数設計のコツ

最後に、C言語でコマンドライン引数を設計する際のベストプラクティスについてまとめます。
まず、引数の数は必要最小限にし、追加機能は-vや--modeのようなオプションで切り替えると分かりやすくなります。
必須引数とオプション引数を明確に区別することが重要です。
また、Usageメッセージを親切に書くことも大切です。
単に「使い方: prog arg1 arg2」とするのではなく、例や説明を加えておくと、ユーザは迷いにくくなります。
エラー時には、何が間違っているのかを明確に伝えるメッセージを出力することが重要です。
たとえば「数値として解釈できません」と具体的に書くことで、ユーザは入力をどのように修正すべきか理解しやすくなります。
さらに、引数の解析部分をparse_args()のような専用関数に分離しておくと、main関数が読みやすくなり、テストもしやすくなります。
大きなプログラムでは、このような構造化が特に重要です。
まとめ
コマンドライン引数は、C言語プログラムに柔軟な入力方法を与える重要な仕組みです。
int main(int argc, char *argv[])という形式を理解し、argcとargvの意味をしっかり押さえることで、文字列表示、数値計算、ファイル処理、オプション解析などさまざまな用途に応用できます。
また、strtolによる安全な数値変換や境界チェック、日本語引数の注意点を意識すれば、より堅牢で使いやすいコマンドラインツールを設計できます。
ここで紹介したサンプルを土台に、自分の用途に合わせた実用的なプログラムへ発展させていってください。
