C 言語で stdout ストリームをフラッシュ

胡金庫 2023年10月12日
  1. C 言語で stdout 出力ストリームをフラッシュするには fflush 関数を使用する
  2. C 言語の printf 関数を用いた fflush の動作をデモンストレーション
C 言語で stdout ストリームをフラッシュ

この記事では、C 言語で stdout 出力ストリームをフラッシュする方法について複数の方法を示します。

C 言語で stdout 出力ストリームをフラッシュするには fflush 関数を使用する

C 標準ライブラリは I/O ライブラリ stdio を提供しており、これはユーザ空間で行われる I/O 操作のバッファリング版を本質的に表しており、一般的なユースケースでのパフォーマンスを向上させています。一般的に、ファイルへのアクセスや操作はオペレーティングシステムのサービスによって提供されます。システムコールの頻繁な呼び出しは、オペレーティングシステムのカーネルデータ構造にアクセスし、制御を前後に転送する必要があるため、プログラムの動作を遅くします。その結果、stdio 関数呼び出しを使用する際の入出力操作を処理するためのバッファが C ライブラリによって維持されています。

ユーザがカーネルバッファへの書き込みを強制的に行う必要がある場合は、fflush 関数で与えられたストリームをフラッシュする必要があります。fflush は与えられたストリームへのポインタ FILE を一つの引数にとる。なお、fflush は出力ストリームに対しては書き込み関数を強制し、入力ストリーム(シーク可能なファイルを含む)に対してはバッファリングされたデータを破棄することに注意してください。引数が NULL の場合、開いているすべての出力ストリームをフラッシュします。

ただし、fflush は書き込まれたデータが物理的に保存されることを保証するものではないことに注意してください。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
  char *username;
  size_t len;
  int lnmax = 256;

  username = malloc(lnmax);
  if (username == NULL) perror("malloc");

  printf("Username: ");
  fflush(stdout);
  if (fgets(username, lnmax, stdin) == NULL) exit(EXIT_FAILURE);

  printf("Your username is set to - %s", username);

  exit(EXIT_SUCCESS);
}

出力:

Username: tmp
Your username is set to - tmp

C 言語の printf 関数を用いた fflush の動作をデモンストレーション

いくつかのストリーム(例:stderr)はバッファリングされていないことに注意してください。対照的に、暗黙的に stdout ストリームに書き込む printf 関数はバッファリングされており、フードの下で繰り返し 1 文字ずつ出力する無限ループを実行すると、内部バッファが一杯になるまでストリームに内容を出力しません。したがって、以下のコード例では、箇条書きの文字をバースト出力していることになります。人間の目がはっきりと観察できるように実行速度を遅くするために、繰り返しのたびに usleep 関数を呼び出していることに注目してください。

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

int main(int argc, char *argv[]) {
  while (1) {
    printf(".");
    usleep(1e3);
  }

  exit(EXIT_SUCCESS);
}

代わりに、printf の呼び出しを fprintf で代用すると、stderr ストリームに出力する fprintf を呼び出すことができ、毎秒毎に繰り返し文字を文字単位で出力する動作が得られるようになります。繰り返しになりますが、繰り返しのたびに 1 秒ずつ遅延させているのは、良いデモンストレーションを確実にするためだけです。

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

int main(int argc, char *argv[]) {
  while (1) {
    fprintf(stderr, ".");
    sleep(1);
  }

  exit(EXIT_SUCCESS);
}

最後に、前の例で見たような stdout ストリームの動作を真似したい場合は、printf 関数の後に fflush 呼び出しを追加することができます。これにより、C ライブラリのバッファがカーネルバッファに書き込まれるたびに強制的にカーネルバッファに書き込まれ、似たような動作をするようになります。

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

int main(int argc, char *argv[]) {
  while (1) {
    printf(".");
    fflush(stdout);
    sleep(1);
  }

  exit(EXIT_SUCCESS);
}
著者: 胡金庫
胡金庫 avatar 胡金庫 avatar

DelftStack.comの創設者です。Jinku はロボティクスと自動車産業で8年以上働いています。自動テスト、リモートサーバーからのデータ収集、耐久テストからのレポート作成が必要となったとき、彼はコーディングスキルを磨きました。彼は電気/電子工学のバックグラウンドを持っていますが、組み込みエレクトロニクス、組み込みプログラミング、フロントエンド/バックエンドプログラミングへの関心を広げています。

LinkedIn Facebook

関連記事 - C IO