C言語基礎|バッファリングとリダイレクト

「改行を押すまで表示されない理由、知ってる?――バッファとリダイレクトで入出力の正体が見える」

入出力が“すぐ動かない”のは、だいたいバッファのせい

C言語で getchar や printf を使っていると、

  • 1文字入力したのに反応が遅い。
  • printf したのにすぐ表示されない。
  • 端末だと動くのに、ファイルにすると挙動が変わる。

みたいなことが起きます。これ、たいてい バッファリング が関係しています。
そして、入力や出力の行き先が変わるのは リダイレクト の仕組みです。

この記事では、入出力が「キーボードと画面に固定」ではなく、
起動時にOS側で差し替えられることも含めて、スッキリ整理しますね。

サンプルプログラム

入力を読みながら行数・文字数を数えて出力するプログラム例です。

プロジェクト名:chap8-12-1 ソースファイル名:chap8-12-1.c

#include <stdio.h>

int main(void)
{
    int ch;
    long chars = 0;
    long lines = 0;

    puts("文章を入力してください(終了は Ctrl+D または Ctrl+Z)。");

    while ((ch = getchar()) != EOF) {
        chars++;
        if (ch == '\n')
            lines++;
    }

    printf("集計しました:文字数=%ld 行数=%ld\n", chars, lines);
    return 0;
}

実行例(端末入力のイメージ)

文章を入力してください(終了は Ctrl+D または Ctrl+Z)。
Hello
World
集計しました:文字数=12 行数=2

バッファリングとは:入出力を“ためてから”まとめて処理する仕組み

C言語の標準入出力は、多くの環境で すぐに入出力しません
読み込んだ文字や、出力する文字をいったん バッファ(ためる箱) に入れて、一定のタイミングでまとめて動きます。

バッファのイメージ

図の説明
入出力は「直接」ではなく「バッファを経由」することが多いです。これが体感のズレ(遅れて表示など)を生みます。

3つの代表的な方式:完全/行/無バッファリング

バッファリング方式の比較

方式いつ実際に入出力される?典型例体感
完全バッファリングバッファが満杯になったとき出力先がファイルのときに起きやすいまとめてドバッと出る
行バッファリング改行が来たとき(\n)端末に対する出力で起きやすい1行単位で出る
無バッファリングすぐに入出力される一部の環境や特別な設定すぐ反応する

表の説明

  • 「改行を押した後にまとめて表示される」なら、だいたい行バッファリングが働いています。
  • ファイルに出すと“さらに遅く見える”のは、完全バッファリング寄りになることがあるからです。

なぜ「1文字入力してもすぐ出ない」ことがあるの?

端末で getchar + putchar のようなプログラムを動かすと、1文字ずつ即出力されそうに見えます。
でも実際は、入力側が行バッファリング的になっていて、Enter(改行)を押すまで入力が届かない環境が多いです。

端末入力が届くタイミング(イメージ)

図の説明
「getchar は1文字読む」のに、プログラムへ渡されるのが行単位になることがある、というギャップがポイントです。

リダイレクトとは:入出力先をOSが差し替える機能

リダイレクトは C言語の機能ではなくOSの機能です。
プログラムが使う「標準入力」「標準出力」を、起動時にファイルへつなぎ替えます。

標準入出力とリダイレクト

名前通常のつながりリダイレクトすると
標準入力(stdin)キーボードファイルから読む
標準出力(stdout)画面ファイルへ書く

表の説明
printf は画面に固定ではなく「標準出力に出す」関数です。
標準出力が画面につながっているから画面に見えているだけなんですね。

リダイレクトの書式と意味

入力リダイレクト

program < input.txt

何をする?
program の標準入力を input.txt に差し替えます。プログラムはキーボードから読んでいるつもりでも、実際はファイルから読めます。

出力リダイレクト

program > output.txt

何をする?
program の標準出力を output.txt に差し替えます。printf の結果が画面ではなくファイルに保存されます。

入力と出力を同時に

program < input.txt > output.txt

何をする?
入力は input.txt、出力は output.txt へ。まさに「ファイルを読んで結果をファイルに書く」形になります。

サンプルで体験:端末実行とリダイレクト実行の違い

さっきの集計プログラム(文字数・行数)を例にすると、こんな使い方ができます。

実行の形(イメージ)

(手入力で試す)
program
→ キーボード入力を数える

(ファイルで試す)
program < input.txt
→ input.txt の内容を数える

(結果を保存する)
program < input.txt > result.txt
→ 集計結果が result.txt に保存される

説明
コードは一切変えていないのに、入力元・出力先が変わります。これがリダイレクトの強さです。

バッファリングとリダイレクトが結びつくポイント

リダイレクトで出力先が「画面→ファイル」に変わると、バッファリング方式が変わって 出力がさらに遅く見えることがあります。

出力先で変わりやすい挙動

出力先起きやすいバッファ方式よくある見え方
端末(画面)行バッファリング寄り改行ごとに出やすい
ファイル(> で保存)完全バッファリング寄りまとめて最後に出る感じ

表の説明
「printf したのに途中経過が出ない!」は、リダイレクトでファイル出力にしたときに起きやすい現象です。

登場する命令・構文の書式と役割(この記事で使ったもの)

getchar の書式

int getchar(void);

何をする?
標準入力から1文字読み、文字コード(int)を返します。読めないと EOF を返します。

puts の書式

int puts(const char *s);

何をする?
文字列を表示して改行します。案内文の表示に便利です。

printf の書式

int printf(const char *format, ...);

何をする?
書式に従って標準出力へ表示します。標準出力が画面なら画面に、ファイルならファイルに出ます。

while の書式

while (条件) { 文; }

何をする?
EOF が来るまで読み続ける、入出力の定番ループを作れます。

まとめ:入出力は「関数」だけでなく「環境」とセットで理解しよう

  • バッファリングは「ためてから動く」仕組みで、改行や満杯がきっかけになる。
  • 端末では行バッファリングっぽく、ファイルでは完全バッファリングっぽく見えることがある。
  • リダイレクトはOSの機能で、stdin/stdout の行き先を起動時に差し替える。
  • printf は画面へ出す関数ではなく、標準出力へ出す関数