【问题标题】:Does each call to printf() create a new internal buffer, or do all subsequent calls use the same buffer as the first?是否每次调用 printf() 都会创建一个新的内部缓冲区,还是所有后续调用都使用与第一次调用相同的缓冲区?
【发布时间】:2018-07-22 01:20:35
【问题描述】:

当我使用printf() 编写一个小程序时,我注意到在随后的printf() 调用中使用的每个\t 字符都从上一个printf() 调用中的字符串末尾继续。

下面的图片和代码示例将阐明我的意思。

预期输出示例

printf("\t%d", i); // Prints multiples of 1
printf("\t%d", i * 2); // Prints multiple of 2
printf("\t%d", i * 3); // ... multiple of 3
printf("\t%d", i * 4); // ... multiple of 4
printf("\t%d", i * 5); // ... multiple of 5
printf("\n"); // Starts new line after each number printed.

要在上图中获得所需的输出,我期望必须这样做:

printf("\t%d", i);
printf("\t\t%d", i * 2);
printf("\t\t\t%d", i * 3);
printf("\t\t\t\t%d", i * 4);
printf("\t\t\t\t\t%d", i * 5);

但这不起作用,后面的每个 printf() 调用都从前一个字符串的末尾继续,这是有道理的,因为我们不使用新行。

最终,我决定一个 printf 调用做同样的事情,虽然可读性稍差,而且开销可能更少。

printf("\t%d\t%d\t%d\t%d\t%d\n", i, i, i * 2, i * 3, i * 4, i * 5);

这让我对 printf() 的工作原理感到好奇。对函数的每个后续调用是否使用第一次调用创建的相同内部缓冲区?

谢谢!

【问题讨论】:

  • 它有什么不同?
  • 处理缓冲的不是printf 调用,而是FILE 流。 printf 函数写入有自己的缓冲区的stdout。实际创建stdout(或任何FILE 流)缓冲区的方式、时间和地点在很大程度上无关紧要。
  • 对于您展示的简单案例,“开销”可以忽略不计。将文本写入控制台可能比六个 printf 调用慢得多。
  • 最后,提前考虑这些事情通常是过早的优化,这往往会导致糟糕的代码。首先要专注于编写好的、可读的和可维护的代码。然后,如果有一些效率要求(并且程序“不够好”(通常足够好)),那么测量、分析和基准测试以找到瓶颈和热点,并专注于最糟糕的那些第一。使用适当的文档和注释(因为优化通常会使代码几乎无法维护)。
  • 使用新的/相同的缓冲区无法解释您所看到的。

标签: c printf c-strings


【解决方案1】:

你的问题是这个

printf("\t%d", i);
printf("\t\t%d", i * 2);
    // other printf elided for brevity

不等于您正在寻找的内容。具体来说,您似乎错误地相信printf() 的每次调用都会将光标返回到行首(例如在终端上)。

实际上,printf() 的第二次调用在第一次打印的整数值之后立即写入同一行。

输出是顺序的,所以

printf("\t%d", i);
printf("\t\t%d", i * 2);     // total of three \t in this and preceding statement

等价于(就完整的数据输出而言)

printf("\t%d\t\t%d", i, i*2);    // note the 3 \t here.

不要

printf("\t%d\t%d", i, i*2);      //  only two \t here

此行为与缓冲无关。

【讨论】:

    【解决方案2】:

    首先printf()不直接处理缓冲内存。假设它使用fwrite()函数调用(它也使用系统调用write())向任何文件写入任何内容,并且这个函数处理缓冲内存。它包含一个pointer 到下一个写入最后一个字符的位置。 fwrite() 函数将数据写入特定文件。printf() 请求 fwrite() 将数据写入名为 stdout 的特定文件中。

    每次调用printf() 函数时,它都会调用fwrite() 函数将数据写入stdout。 假设你想打印"Hello World",你可以用printf的一次调用来打印它

    printf("Hello World\n");
    

    printf("Hello ");
    printf("World");
    printf("\n");
    

    所以你可以用 3 printf 调用它,这将调用 fwrite() 3 次,这个函数将记住接下来要写入数据的位置。例如:- 在最后一次调用 printf("\n") 时,该位置将设置到下一行。

    【讨论】:

    • 您确定您声称printf() 使用fwrite() 吗?
    • @JonathanLeffler 不,我只是在说明如何解释它。
    • 这不是你的回答所说的。您的回答说“[printf()] 使用了fwrite() 调用”,而没有警告“您可以认为它的行为就像……”或类似的东西。
    • printf 不一定调用fwrite(实际上在我熟悉的实现中,它根本不会调用它;它使用fputc)。
    • '这很有趣。 POSIX 规范通常标记其自身与 C 标准之间的差异,但 fprintf() 表示 “由 fprintf()printf() 生成的字符被打印为好像 fputc() 已被调用。” '不要为此添加任何标记,但它不存在于 C11 标准 (C11 §7.21.6.1 The fprintf function) 中。注意最重要的“好像”。 C99 也没有提到,AFAICT,C90 也没有。
    【解决方案3】:

    Printf 的打印效果与您手动输入内容完全一样。除非您明确告诉它这样做,否则它不会回到行首。所以当 printf 被要求打印 "\ta" 和 "\tb" 时,它首先输出

        a|
         ^- And leaves the "cursor" here...
    

    所以当你要求它打印“\tb”时,它会从中断的地方继续,并打印:

        a    b
    

    【讨论】:

      猜你喜欢
      • 2020-03-25
      • 1970-01-01
      • 2016-02-16
      • 1970-01-01
      • 2013-06-09
      • 2020-06-05
      • 1970-01-01
      • 2013-03-18
      • 1970-01-01
      相关资源
      最近更新 更多