write()向标准输出(stdout)和printf输出的内容不交错?

9
#include <stdio.h>
#define MAXLEN 256

int main() {
  int n;
  char buf[MAXLEN];
  while((n = read(0,buf,sizeof(buf))) != 0){
    printf("n: %d:",n);
    write(1,buf,n);
  }
  return 1;
}

程序的输出结果(其中第一个read和第一个write由用户键入并由终端回显)是:
read
read
write
write
n: 5:n: 6:
printf的输出是在标准输入中按下Ctrl+D之后才出现,而不是与随后的读取一起出现。为什么会这样?
5个回答

22

Printf是有缓冲区的。

你可以使用fflush调用来强制printf'刷新'其缓冲区:

#include <stdio.h>
#define MAXLEN 256

int main() {
  int n;
  char buf[MAXLEN];
  while((n = read(0,buf,sizeof(buf))) != 0){
    printf("n: %d:",n);
    fflush(stdout); /* force it to go out */
    write(1,buf,n);
  }
  return 1;
}

printf() 通常是缓冲的,这是一件好事情。特别是在需要屏幕更新等可见控制台上,不缓冲的输出会很慢。如果一个应用程序频繁使用 printf,它的速度会直接受到影响(特别是在 Windows 平台上;Linux 和 Unix 受到的影响通常较小)。

然而,如果你同时使用了 fprintf(stderr,)printf() 的缓冲就会成为问题 —— stderr 是有意不缓冲的。因此,你可能会发现一些 printf() 缺失了。如果你要写入另一个与终端相关联且可能未被缓冲的 FILE 句柄,请确保首先显式地调用 fflush(stdout)


7
在执行任何输入输出操作之前,您可以使用setvbuf()更改缓冲模式。 - AProgrammer
"printf()缓冲的"是什么意思? - ma11hew28
1
@MattDiPasquale printf函数输出到标准输出(stdout),而stdout默认是带缓冲的。只有在换行符时才会刷新缓冲区。 - Will
1
还应该提到,换行符('\n')会导致标准输出被刷新。 - carefulnow1
@carefulnow1:这取决于它指向哪里。如果它指向文件,通常是块缓冲的,只有当它指向终端时才是行缓冲的。 - ShadowRanger

2

fgets的手册告诉我:

不建议将stdio库中的输入函数调用与使用与输入流相关联的read(2)低级调用混合使用;结果将是未定义的,并且很可能不是您想要的。

因此,最好的解决方案是不要在同一描述符上同时使用write和printf。


POSIX非常仔细地指定了基于“活动句柄”的抽象概念,何时结果是未定义的,何时是明确定义的。请参见http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_05_01。 - R.. GitHub STOP HELPING ICE

1

Printf使用stdio并且是缓冲的。 通过发送“n:%d:\n”的更改来推出它。


要么这样做,要么就不要混合输出通道--也就是说,使用一个函数来输出所有内容。 - Kim Gräsman
1
"\n不能保证刷新缓冲区。" - EFraim
1
标准输出(stdout)除非被重定向到一个非交互设备,否则它是行缓冲的。 - AProgrammer

1

您可以使用std fflush()函数来刷新std out缓冲区,或者在printf内部的控制字符串末尾使用额外的\n。就像这样:

printf("\n :%d:\n",n);

在C语言中,使用write()和read()函数而不是printf()和scanf()函数总是更好的选择。Printf和scanf存在一些问题,例如printf将字符串参数存储在stdout缓冲区中。因此需要手动刷新,可以通过fflush函数或\n来完成。在一个小的hello world打印程序中,您不会发现这样的问题,因为stdout缓冲区在程序执行结束时被刷新。最好使用正常工作的write()函数。scanf也存在读取空格和许多其他与stdin缓冲区相关的问题。

例如下面的代码:

main()  {   char a; int i=0,c; for(;i<2;i++) { scanf("%d",&c); scanf("%c",&a);} }

以上程序存在一个问题,即在按下回车键时将\n读入stdin。我们可以通过不刷新stdin缓冲区或使用\n字符来解决这个问题。最好始终使用read()和write()函数。
希望这有所帮助...

0
使用fwrite(流版本)而不是write。
请注意,虽然stdout与文件号1相关联,但它并不是同一件事。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接