【问题标题】:Regarding printing characters in C关于在C中打印字符
【发布时间】:2014-03-30 09:39:21
【问题描述】:
int main()
{
    printf("Hello"); // doesn't display anything on the screen

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

    return 0;
}

所有字符(打印的候选)都被缓冲直到收到一个新行?对吗?

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

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

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

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

寻找深入的图片。如果有什么不合理的地方,请随时编辑问题。

【问题讨论】:

  • 要回答您未标记的问题,这取决于系统和您的设置。有时甚至换行符都不会导致刷新,但执行fflush(stdout) 会强制刷新。
  • 我希望我早点学习 fflush

标签: c printf puts


【解决方案1】:

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

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

setbuf(stdout, NULL);

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

【讨论】:

  • 您将行缓冲(例如,当连接到终端时)和完全缓冲(例如,当输出到文件时)混合在一起,它们是不一样的。遇到新行时刷新只是行缓冲流的行为。
  • 这取决于您使用的操作系统。在 UNIX 环境中,通常没有太大区别,您也可以在终端会话中看到这种效果。在 Windows 中,当打印到控制台时,通常总是直接打印,但这可能因实现而异,不应依赖。
  • 不,它们根本不一样。例如,将标准输出重定向到文件,即使您添加新行字符也不会刷新内容,仅当缓冲区已满或明确要求时才会刷新(使用fflush())。不同的实现可能有不同的默认缓冲区类型。但是这三种缓冲区类型的行为是在标准 C 中定义的。
  • 什么是输出流?它是如何实施的?它在哪里实施?
  • @codeymodey,该流在您链接的 C 库中实现。
【解决方案2】:

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

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

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

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

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

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

stdinstdout 被块缓冲当且仅当它们可以被确定不引用交互式设备,否则它们被行缓冲(对于任何流都是如此)。默认情况下,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;
}

【讨论】:

  • 这是一个很好的答案,您能否还包括一些相关代码和文档的参考资料以供阅读。
  • 谢谢 :) 会在我下班时包含资源和参考资料的相关链接。
【解决方案3】:

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

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

setbuf(stdout, NULL);

printfputsputchar的所有函数都输出到标准输出,所以它们使用同一个缓冲区。

【讨论】:

  • 见上面Devolus的回答,他说stdout是在C库中实现的。你们在这一点上是相同的还是不同的?
【解决方案4】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-09
    • 2013-02-03
    • 1970-01-01
    • 2022-11-26
    • 2016-11-21
    • 1970-01-01
    • 2013-11-23
    相关资源
    最近更新 更多