C言語ではプログラムが外の世界とやり取りするために、標準入力(stdin)、標準出力(stdout)、標準エラー出力(stderr)という3つのストリームが最初から用意されています。
これらの役割と基本的な使い方を理解することは、入門の大きな一歩です。
本記事では、初心者の方にも分かるように、読み書きの基本とバッファ、リダイレクトまで丁寧に解説します。
stdin stdout stderrの基本
標準ストリームは、ヘッダstdio.h
で定義されている3つのFILE*
ポインタです。
通常は次のように端末(TTY)に接続されていますが、リダイレクトするとファイルやパイプなどに切り替わります。
標準入力(stdin)とは
標準入力(stdin)は、プログラムが外部からデータを受け取る入り口です。
キーボード入力やファイル、パイプからデータを読み込みます。
型はFILE*
で、識別子はstdin
です。
代表的な用途
ユーザーに数値や文字列の入力を求めたり、別プログラムの出力をパイプで受け取るときに使います。
関数としてはscanf
やgetchar
、fgets
などが使われます。
初心者が押さえるポイント
入力関数の戻り値を必ず確認しましょう。
失敗したまま進むと未定義の動作を招きやすいです。
標準出力(stdout)とは
標準出力(stdout)は、プログラムの通常の結果を表示する出口です。
型はFILE*
で、識別子はstdout
です。
代表的な用途
計算結果やメッセージを画面に表示する、あるいはリダイレクトでファイルに書き出す用途に使います。
関数はprintf
、puts
、putchar
などです。
標準エラー出力(stderr)とは
標準エラー出力(stderr)は、エラーや警告などのメッセージ専用の出口です。
型はFILE*
で、識別子はstderr
です。
代表的な用途
入力エラー、ファイルオープン失敗、使い方の誤りなどの通知に使います。
fprintf(stderr, ...)
やperror
がよく使われます。
stdoutとstderrの違い
- 目的の違い: stdoutは通常の結果、stderrはエラー通知に使います。
- リダイレクトの独立性: stdoutをファイルにリダイレクトしても、stderrはデフォルトでは別扱いです。結果とエラーを別ファイルに分けられます。
- バッファリングの違い: 多くの実装では、端末接続時のstdoutは行バッファリング、stderrは無バッファ(すぐ表示)です。ファイルにリダイレクトされるとstdoutは全バッファ、stderrは行バッファや無バッファになることがあります。
次の表に3ストリームの要点をまとめます。
ストリーム | 識別子 | 方向 | 典型的な接続先 | 主な関数 | 既定のバッファリング(典型) |
---|---|---|---|---|---|
標準入力 | stdin | 入力 | キーボード、ファイル、パイプ | scanf 、getchar 、fgets | 行バッファ/全バッファ |
標準出力 | stdout | 出力 | 画面、ファイル、パイプ | printf 、puts 、putchar | 行バッファ(端末)または全バッファ(ファイル) |
標準エラー | stderr | 出力 | 画面 | fprintf 、perror | 無バッファまたは行バッファ |
バッファリングは実装や環境で異なる場合があります。
絶対にこうなる、と決め打ちしないことが重要です。
stdoutに出力する基本
printfでstdoutに表示
printf
はフォーマット文字列に従ってstdoutへ出力します。
戻り値は出力した文字数(エラー時は負数)です。
// printf_stdout.c
#include <stdio.h>
int main(void) {
// %d, %f, %s などのフォーマット指定で整形して表示します
int answer = 42;
double pi = 3.14159;
const char *name = "C beginner";
// \n を付けると行バッファの場合すぐ表示されやすいです
int n = printf("Hello, %s! answer=%d, pi=%.2f\n", name, answer, pi);
// 戻り値の確認もできます
if (n < 0) {
// 出力に失敗した場合
fprintf(stderr, "printfに失敗しました\n");
return 1;
}
return 0;
}
Hello, C beginner! answer=42, pi=3.14
フォーマット指定の基本
%d
: 整数%.2f
: 小数点以下2桁の浮動小数点%s
: 文字列\n
: 改行
puts/putcharの簡単出力
puts
は文字列を出力して自動で改行を付けます。
putchar
は1文字を出力します。
どちらもstdoutに書き込みます。
// puts_putchar.c
#include <stdio.h>
int main(void) {
puts("1行目です"); // 末尾に自動で \n が付く
puts("2行目も自動改行");
putchar('A'); // 1文字出力
putchar('\n'); // 自分で改行を付ける
return 0;
}
1行目です
2行目も自動改行
A
改行とバッファとfflush
バッファリングにより、出力は一時的にメモリに蓄えられてからまとめて表示されることがあります。
端末に接続されているstdoutは行バッファリングが多く、\n
を出力したタイミングで表示されやすいです。
長い処理中に進捗を表示したいときはfflush(stdout)
で強制的にフラッシュします。
// progress_fflush.c
#include <stdio.h>
int main(void) {
printf("処理中"); // \n を付けていないので、環境によっては見えないことがある
fflush(stdout); // ここで強制的に表示
for (int i = 0; i < 3; ++i) {
// 時間のかかる処理のつもりで重い計算を少し回します
volatile unsigned long x = 0;
for (unsigned long j = 0; j < 50000000UL; ++j) {
x += j; // 何か計算することで最適化されにくくする
}
printf("."); // 進捗をドットで表示
fflush(stdout); // すぐに見せたいのでフラッシュ
}
printf("\n完了\n");
return 0;
}
処理中...
完了
fflush(NULL)
は全出力ストリームをフラッシュします。
fflush(stdin)
は未定義動作なので使用しないでください(一部処理系では拡張として動作しますが、移植性がありません)。
stdinから入力する基本
scanfで数値や文字列を読む
scanf
は書式に従って標準入力から値を読み取ります。
戻り値は成功した項目数です。
失敗検出に必ず使いましょう。
// scanf_basic.c
#include <stdio.h>
int main(void) {
int age;
char name[100];
printf("年齢を入力してください: ");
if (scanf("%d", &age) != 1) { // 戻り値チェック
fprintf(stderr, "年齢の読み取りに失敗しました\n");
return 1;
}
printf("名前を入力してください(空白なし): ");
if (scanf("%99s", name) != 1) { // 幅指定でバッファあふれ対策
fprintf(stderr, "名前の読み取りに失敗しました\n");
return 1;
}
printf("こんにちは、%sさん(%d歳)!\n", name, age);
return 0;
}
年齢を入力してください: 23
名前を入力してください(空白なし): Taro
こんにちは、Taroさん(23歳)!
%s
は空白で区切られます。
スペースを含む文字列を安全に読みたい場合はfgets
の利用を検討してください(本記事では扱いません)。
getcharで1文字読む
getchar
は1文字をint
で返します。
EOF判定のためint
で受けるのが正解です。
// getchar_one.c
#include <stdio.h>
int main(void) {
int ch;
printf("1文字入力してください: ");
ch = getchar(); // 1文字だけ読み取る
if (ch == EOF) {
fprintf(stderr, "EOFが入力されました\n");
return 1;
}
printf("あなたが入力したのは '%c' です(コード: %d)\n", ch, ch);
return 0;
}
1文字入力してください: A
あなたが入力したのは 'A' です(コード: 65)
入力の改行と戻り値の確認
数値入力のあとにgetchar
で1文字を読むと、直前の改行\n
が残っていて即座に読まれてしまうことがあります。
この場合は改行を捨てる処理を入れましょう。
// consume_newline.c
#include <stdio.h>
int main(void) {
int n;
printf("整数を入力してください: ");
if (scanf("%d", &n) != 1) {
fprintf(stderr, "整数の読み取りに失敗しました\n");
return 1;
}
// 改行を捨てる: scanfは末尾の改行を消費しない場合がある
int c;
while ((c = getchar()) != '\n' && c != EOF) {
// 何もしない(残りを捨てる)
}
printf("続けますか? y/n: ");
int ans = getchar();
if (ans == EOF) {
fprintf(stderr, "入力が得られませんでした\n");
return 1;
}
if (ans == 'y' || ans == 'Y') {
printf("続行します\n");
} else {
printf("中止します\n");
}
return 0;
}
整数を入力してください: 10
続けますか? y/n: y
続行します
fflush(stdin)
で入力を捨てるのはNGです。
標準Cでは未定義動作なので、上のようにgetchar
で読み捨てましょう。
stderrでエラー出力する基本
エラーはstderrに出す理由
エラーと正常結果を分離しておくと、ユーザーは正常な結果だけをファイルに保存し、エラーだけを画面で確認できます。
ログ収集や自動化でも扱いやすくなります。
fprintf(stderr, …)でエラー表示
fprintf
で宛先にstderr
を指定します。
使い方はprintf
と同様です。
// use_stderr.c
#include <stdio.h>
int main(int argc, char *argv[]) {
if (argc < 2) {
// 使い方の誤りはstderrへ
fprintf(stderr, "使い方: %s <ファイル名>\n", argv[0]);
return 2; // コマンドの使い方エラーを示すコード
}
printf("指定されたファイル名は: %s\n", argv[1]); // 通常の情報はstdout
return 0;
}
$ ./use_stderr
使い方: ./use_stderr <ファイル名> ← これはstderrに出ています
$ ./use_stderr data.txt
指定されたファイル名は: data.txt ← これはstdoutに出ています
perrorでシステムエラーを表示
perror
は直近のシステムエラーerrno
を、分かりやすいメッセージにしてstderrへ出力します。
// perror_example.c
#include <stdio.h>
int main(void) {
// 存在しないファイルを開いてみる
FILE *fp = fopen("not_exist.txt", "r");
if (fp == NULL) {
// "fopen: No such file or directory" のように出力
perror("fopen"); // 引数はプレフィックス。NULLでも可
return 1;
}
fclose(fp);
return 0;
}
fopen: No such file or directory
より細かく制御したい場合は#include <string.h>
のstrerror(errno)
で文字列を取り出せます。
リダイレクトでstdoutとstderrを分ける
コマンドラインのリダイレクトで、stdoutとstderrを別々のファイルへ出力できます。
Unix系シェル(Bashなど)
- 通常出力だけを保存:
./prog > out.txt
- エラーだけを保存:
./prog 2> err.txt
- 両方を別々に保存:
./prog > out.txt 2> err.txt
- 両方を1つにまとめる:
./prog > all.txt 2>&1
または./prog 2>&1 | tee all.txt
Windows
- CMD:
prog.exe 1> out.txt 2> err.txt
- PowerShell:
.\prog.exe *> out.txt
は全ストリーム。分けるなら.\prog.exe > out.txt 2> err.txt
次の小さなプログラムは、stdoutとstderrに同時に出力します。
// split_streams.c
#include <stdio.h>
int main(void) {
printf("これはstdoutのメッセージです\n");
fprintf(stderr, "これはstderrのメッセージです\n");
return 0;
}
$ ./split_streams > out.txt
これはstderrのメッセージです ← 画面に表示(エラーはそのまま)
$ cat out.txt
これはstdoutのメッセージです
$ ./split_streams > out.txt 2> err.txt
$ cat out.txt
これはstdoutのメッセージです
$ cat err.txt
これはstderrのメッセージです
まとめ
標準入力(stdin)、標準出力(stdout)、標準エラー出力(stderr)は、プログラムの入出力の基本土台です。
stdoutは通常の結果、stderrはエラーや警告という役割分担を守ることで、リダイレクトやログ収集が扱いやすくなります。
出力時は改行とバッファの関係を意識し、必要に応じてfflush(stdout)
を使って表示タイミングを制御しましょう。
入力では必ず戻り値を確認し、改行の扱いに注意することで予期しない動作を防げます。
さらに、エラー時はfprintf(stderr,...)
やperror
を使い分け、リダイレクトで出力を分離する実践を身につければ、堅牢で扱いやすいCプログラムを書けるようになります。