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);
}