在 C 语言刷新 stdout 输出流

本文将演示关于如何在 C 语言中刷新 stdout 输出流的多种方法。

在 C 语言中使用 fflush 函数来刷新 stdout 输出流

C 标准库提供了一个 I/O 库,即 stdio,它基本上代表了在用户空间中进行的 I/O 操作的缓冲版本,从而提高了常见用例的性能。一般来说,访问文件和对文件进行操作是由操作系统服务提供的,因此,用户最终需要系统调用,例如打开一个文件。频繁的系统调用会使程序的运行速度变慢,因为它需要访问操作系统的内核数据结构并来回传输控制。因此,在使用 stdio 函数调用时,C 库会维护一些缓冲区来处理输入/输出操作。

如果用户需要对内核缓冲区进行强制写入,则需要对 fflush 函数提供的给定流进行刷新。fflush 需要一个 FILE 的单参数,指向给定流的指针。请注意,fflush 对输出流强制执行写功能,同时丢弃输入流的任何缓冲数据(有可寻文件)。如果参数是 NULL,它将刷新所有打开的输出流。

注意,fflush 并不能确保写入的数据被物理存储,因为这需要内核缓冲区被刷新(可以使用 fsync 调用来完成,请参见这里)。

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <time.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 函数是有缓冲的,如果我们在下面执行每次迭代打印一个字符的无限循环,直到内部缓冲区满了才会向流输出内容。因此,下面的代码示例的结果是突发打印 bullet 字符。请注意,我们每次迭代都调用 usleep 函数,以减慢人眼的执行速度,以便于清晰地观察到。

#include <stdio.h>#include <stdlib.h>#include <unistd.h>int main(int argc, char *argv[]) {
    while (1) {
        printf(".");
        usleep(1e3);
    }
    exit(EXIT_SUCCESS);
}

另外,如果我们用 fprintf 代替 printf 的调用,打印到 stderr 流,就会产生每秒钟一个字符的迭代打印行为。再次强调,每次迭代的 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);
}