为什么在sleep()之前printf()没有输出任何内容?

25

我正在学习 Kernighan 和 Ritchie 的书中的 C 语言;我正在第四章(“函数和程序结构”)的基础知识中。有一天,我对 sleep() 函数产生了好奇心,所以尝试使用它,像这样:

#include <stdio.h>
#include <unistd.h>

int main(void)
{
  printf(" I like cows.");
  sleep(5);
  return 0;
}
程序的问题在于输出,看起来它先执行了sleep()再执行printf(),换句话说,它等待5秒钟然后才打印字符串。所以我想,也许程序执行sleep()太快了,没有让printf()像我想要的那样完成它的工作,即先打印字符串,然后再休眠。

我该如何先显示字符串,然后让程序休眠呢? 编译器是OpenBSD 4.3中的GCC 3.3.5(propolice)。

4个回答

51

printf()将输出到stdout(默认输出流),它通常是行缓冲的。由于缓冲区在调用sleep时没有刷新,因此不会显示任何内容。当程序退出时,所有流都会自动刷新,这就是为什么在退出前才打印出来。打印一个换行符通常会导致流被刷新,或者您可以使用fflush函数:

int main(void)
{
  printf(" I like cows.\n");
  sleep(5);
  return 0;
}

或:

int main(void)
{
  printf(" I like cows.");
  fflush(stdout);
  sleep(5);
  return 0;
}

如果您正在向一个非行缓冲的流打印(例如,如果stdout被重定向或者您正在写入文件),仅仅打印一个换行符可能不起作用。在这种情况下,如果您想要立即写入数据,应该使用fflush


将字符串添加 \n 的原因是由于向控制台输出的 printf 是行缓冲的。然而,如果将其重定向到文件,则可能不足够,因为它使用了不同的缓冲方案。 - Paul Tomblin
有趣的问题是,你如何判断当前文件指针使用的缓冲模式? - Vinko Vrsalovic
@Paul,没错,这就是为什么我说它通常会导致流被刷新,但我还是会进一步澄清一下。 - Robert Gamble
@Vinko,没有标准函数可以告诉你流的缓冲模式,但许多实现都有自己的函数(例如glibc有__flbf和__fbufsize)。 - Robert Gamble
@RobertGamble,嗯,我学到了fflush(stdout)是未定义行为。 - Box Box Box Box

9
你的问题是,printf(和其他使用stdio库向标准输出写入东西的内容)是有缓冲区的——如果输出到控制台,则是按行缓冲,如果输出到文件,则是按大小缓冲。如果在printf后加上fflush(stdout);,它就会执行你想要的操作。你可以尝试在字符串中添加换行符('\n'),只要不将标准输出重定向到文件中,那么这样做也是正确的。
我不是100%确定,但我认为stderr没有缓冲区,这可能会导致混淆,因为您可能会看到您之前输出到stdout的输出内容,然后才会看到您输出到stderr的输出内容。

printf 函数没有缓冲区,它所写入的流是有缓冲区的。 - Robert Gamble
标准错误通常是无缓冲的(尽管它可能是行缓冲的)。 - Robert Gamble
为什么换行符('\n')会起作用? - gonidelis
@johngonidelis 因为正如我之前所说,它是行缓冲的。当它看到一行的结尾时,它会刷新缓冲区。 - Paul Tomblin

6
缓冲意味着所有输出都存储在一个地方(称为缓冲区),并在其中存在一定量的数据后输出。这是为了提高效率而做的。
某些(大多数?)实现在向控制台写入时在换行符后清除缓冲区,因此您也可以尝试。
printf(" I like cows.\n");

而不是调用fflush()函数


1
我按照以下方式实现了时间遭遇;
for (int i = 1; i <= 60; i++) {
    printf("%02d", i);
    fflush(stdout);
    sleep(1);
    printf("\b\b");
}

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