1
int main()
{
    printf("Hello"); // doesn't display anything on the screen

    printf("\n"); // hello is display on the screen

    return 0;
}

所有字符(打印的候选)都被缓冲,直到收到新行?正确的?

Q1 - 为什么在终端打印之前要等到换行符?

Q2 - 第一个 printf (ie) 的字符在哪里"Hello"缓冲?

Q3 - 印刷流程是什么printf()->puts()->putchar()-> 现在在哪里?司机?驱动程序是否有控制权等到\n?

Q4 - 附加到进程的标准输出的角色是什么?

求深度图。如果有些事情没有意义,请随时编辑问题。

4

4 回答 4

3

printf不是直接写入屏幕,而是写入默认缓冲的输出流。这样做的原因是,甚至可能没有附加屏幕,并且输出也可以转到文件。出于性能原因,如果对磁盘的访问被缓冲,然后使用适当大小的块在一个步骤中执行,而不是每次都写入,这对系统来说会更好。

您甚至可以更改缓冲区的大小并将其设置为 0,这意味着所有输出都直接发送到目标,这对于日志记录可能很有用。

setbuf(stdout, NULL);

当缓冲区已满或某些条件已满时(例如打印换行符),缓冲区将被刷新。因此,当您在循环中执行 printf 时,您会注意到它将以块的形式写入,除非您之间有换行符。

于 2014-02-27T06:12:37.310 回答
1

我将从一些定义开始,然后继续回答您的问题。

文件:它是一个有序的字节序列。它可以是磁盘文件、程序生成的字节流(如管道)、TCP/IP 套接字、从外围设备接收或发送到外围设备(如键盘或显示器)的字节流等. 后两者是交互文件。文件通常是程序与其环境进行通信的主要方式。

:它表示从一个地方另一个地方的数据流,例如,从磁盘到内存、内存到磁盘、一个程序到另一个等。流是可以将数据放入(写入)或从(读取)中取出数据。因此,它是一个用于将数据写入文件或从文件读取数据的接口,该文件可以是上述任何类型的文件。在您可以对文件执行任何操作之前,必须先打开该文件。打开文件会将其与流相关联。流由标头中FILE定义的数据类型表示stdio.h。一个FILE对象(它是一个结构)保存了有关与关联文件的连接的所有内部状态信息,包括文件位置指示符和缓冲信息等。FILE对象由输入/输出库函数在内部分配和管理,您不应该尝试创建自己的FILE类型对象,库会为我们完成。程序应该只处理指向这些对象的指针(FILE *)而不是对象本身。

缓冲区:缓冲区是属于流的一块内存,用于临时保存流数据。当文件上发生第一次 I/O 操作时,malloc会调用 并获得缓冲区。写入流的字符通常会累积在缓冲区中(在以块的形式传输到文件之前),而不是在应用程序输出它们时立即出现。类似地,流以块的形式而不是逐个字符地从主机环境中检索输入。这样做是为了提高效率,因为与内存操作相比,文件和控制台 I/O 速度较慢。

C库提供了三个预定义的文本流 ( FILE *),它们在程序启动时打开并可供使用。它们是stdin(标准输入流,程序的正常输入源),stdout(标准输出流,用于程序的正常输出)和stderr(标准错误流,用于错误消息和程序发出的诊断信息)。这些流是缓冲的还是非缓冲的是实现定义的,而不是标准要求的。

GCC提供三种类型的缓冲——无缓冲、块缓冲和行缓冲。无缓冲意味着字符在写入后立即出现在目标文件中(对于输出流),或者从文件中逐个字符地读取输入,而不是读取块(对于输入流)。块缓冲意味着字符保存在缓冲区中并作为块写入或读取。行缓冲意味着字符仅保存到换行符写入缓冲区或从缓冲区读取。

stdin并且stdout当且仅当可以确定它们不引用交互式设备时才进行块缓冲,否则它们是行缓冲的(对于任何流都是如此)。stderr默认情况下总是无缓冲的。

标准库提供了改变流默认行为的函数。您可以使用fflush强制数据从输出流缓冲区(fflush输入流未定义)。您可以使用该setbuf函数使流无缓冲。

现在,让我们来回答您的问题

未标记的问题:是的,因为stdout通常指的是显示终端,除非您使用>运算符进行输出重定向。

Q1:它等待是因为stdout在引用终端时缓冲了换行符。

Q2:字符被缓冲在分配给stdout流的缓冲区中。

Q3:打印流程为:内存-->stdout缓冲区-->显示终端。操作系统也控制了内核缓冲区,数据在出现在终端之前会通过这些缓冲区。

Q4:stdout指标准输出流,通常是终端。

最后,这是一个示例代码,用于在我完成答案之前进行实验。

#include <stdio.h>
#include <limits.h>

int main(void) {
    // setbuf(stdout, NULL);     // make stdout unbuffered
    printf("Hello, World!");     // no newline
    // printf("Hello, World!");  // with a newline

    // only for demonstrating that stdout is line buffered

    for(size_t i = 0; i < UINT_MAX; i++)
        ;                        // null statement

    printf("\n");                // flush the buffer
    return 0;
}
于 2014-02-27T21:35:40.973 回答
1

如果您愿意,您可以通过调用清除新行之前的字符

fflush(stdout);

如果您正在缓慢打印诸如进度条之类的内容,其中每个字符在没有换行符的情况下打印,这可能会很方便。

int main()
{
    printf("Hello"); // Doesn't display anything on the screen
    fflush(stdout);  // Now, hello appears on the screen
    printf("\n");    // The new line gets printed
    return 0;
}
于 2014-02-27T06:18:03.823 回答
1

是的,默认情况下,标准输出在连接到终端时是行缓冲的。缓冲区由操作系统管理,通常您不必担心。

setbuf()您可以使用或setvbuf()更改此行为,例如,将其更改为无缓冲区:

setbuf(stdout, NULL);

printf, puts,的所有函数都putchar输出到标准输出,因此它们使用相同的缓冲区。

于 2014-02-27T06:12:43.773 回答