C语言printf的一个刁钻Bug

 
C语言中的 printf() 有一个尴尬的问题,就是有时候不能立即输出,请看下面的代码:
#include<stdio.h>
#include<unistd.h> //sleep()所在的头文件
int main()
{
    printf("C语言中文网");
    sleep(5);  //程序暂停5秒钟
    printf("http://c.biancheng.net\n");

    return 0;
}
这段代码使用了两个 printf() 语句,它们之间有一个 sleep() 函数,该函数的作用是让程序暂停 5 秒,然后再继续执行。

sleep() 是 Linux 和 macOS 下特有的函数,不能用于 Windows。当然,Windows 下也有功能相同的暂停函数,叫做 Sleep(),稍后我们会讲解。

注意开头的字母s,它们的大小写是不一样的。

在 Linux 或者 macOS 下运行该程序,会发现第一个 printf() 并没有立即输出,而是等待 5 秒以后,和第二个 printf() 一起输出了,请看下面的动图演示:


我们不妨修改一下代码,在第一个 printf() 的最后添加一个换行符,如下所示:

printf("C语言中文网\n");

再次编译并运行程序,发现第一个 printf() 首先输出(程序运行后立即输出),等待 5 秒以后,第二个 printf() 才输出,请看下面的动图演示:


为什么一个换行符\n就能让程序的表现有天壤之别呢?按照通常的逻辑,程序运行后第一个 printf() 应该立即输出,而不是等待 5 秒以后再和第二个 printf() 一起输出,也就是说,第二种情形才符合我们的惯性思维。然而,第一种情形该如何理解呢?

其实,这一切都是输出缓冲区(缓存)在作怪!

从本质上讲,printf() 执行结束以后数据并没有直接输出到显示器上,而是放入了缓冲区,直到遇见换行符\n才将缓冲区中的数据输出到显示器上。更加深入的内容,我们将在本章的《进入缓冲区(缓存)的世界,破解一切与输入输出有关的疑难杂症》中详细讲解。

以上测试的是 Linux 和 macOS,我们不妨再测试一下 Windows,请看下面的代码:
#include<stdio.h>
#include<Windows.h> //Sleep()所在的头文件
int main()
{
    printf("C语言中文网");
    Sleep(5000);  //程序暂停5秒钟
    printf("http://c.biancheng.net\n");

    return 0;
}
在 Windows 下,想让程序暂停可以使用 Windows.h 头文件中的 Sleep() 函数(S要大写),它和 Linux 下的 sleep() 功能相同。不过,sleep() 要求的时间单位是秒,而 Sleep() 要求的时间单位是毫秒,1 秒等于 1000 毫秒。这段代码中,我们要求程序暂停 5000 毫秒,也即 5 秒。

注意:sleep() 和 Sleep() 都不是C语言的标准函数,而是 Linux 和 Windows 自带的函数。也就是说,C语言本身并不支持它们,只有操作系统才支持它们。

编译并运行程序,会发现第一个 printf() 首先输出(程序运行后立即输出),等待 5 秒以后,第二个 printf() 才输出,请看下面的动画演示:


在第一个 printf() 的最后添加一个换行符,情况也是一样的,第一个 printf() 从来不会和第二个 printf() 一起输出。

你看,Windows 和 Linux、macOS 的情况又不一样。这是因为,Windows 和 Linux、macOS 的缓存机制不同,Windows 下的 printf() 函数不带缓冲区。更加深入的内容,我们将在本章的《进入缓冲区(缓存)的世界,破解一切与输入输出有关的疑难杂症》中详细讲解。

要想破解 printf() 输出的问题,必须要了解缓存,它能使你对输入输出的认识上升到一个更高的层次,以后不管遇到什么疑难杂症,都能迎刃而解。可以说,输入输出的“命门”就在于缓存。