Skip to main content

C语言标准IO的缓冲区详解 2022-07-31

详细的缓冲区介绍请看这篇文章,非常详细 https://www.cnblogs.com/lanhaicode/p/10575049.html

全缓冲:当缓冲区被填满以后才进行真正的输入输出操作

行缓冲:当在输入或者输出的过程中遇到换行符时,才执行真正的输入输出操作

无缓冲:没有缓冲区,立即进行输入输出

实际上printf是行缓冲,如果printf("hello")而不加上\n是不会出现结果的(之前在fork()的时候遇到这个问题)

但为什么我们直接看结果是会出现的呢?

原因是标准输出在程序正常退出的时候,会调用 fclose(stdout),而 fclose 函数会调用 fflush。因此,如果你的程序提前崩溃了,那是看不到缓冲区中的内容打印到控制台上的。特别注意的是,exit(正常退出)的话会输出,exit在调用系统_exit 之前,首先调用退出处理函数,刷新I/O缓冲,关闭文件描述符,最后调用_exit 退出进程;_exit(立即终止进程)不会输出缓冲区。

https://blog.csdn.net/takashi77/article/details/108077328

fork试验下printf失效的原理

fork()系统调用会创建一个新的进程,这个新进程是调用进程的一个复制。当使用fork()时,如果在fork()之前有未刷新的缓冲数据,这些数据会被复制到子进程中。这可能导致一些意想不到的行为,特别是在使用printf()函数时。 让我们通过一个简单的例子来说明:

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

int main() {
printf("Hello");
fork();
printf("123123");
printf(" World\n");
return 0;
}
Hello123123 World
Hello123123 World

这是因为:

当调用 fork() 时,整个进程(包括缓冲区中的数据)会被复制到子进程中。因此,子进程和父进程各自都有一个包含 "Hello" 的缓冲区。

在 fork() 之后,父进程和子进程都会继续执行之后的代码:由于缓冲区中已经有 "Hello",所以每个进程都会先输出 "Hello",然后是 "123123",最后是 " World\n"。这导致了 "Hello123123 World" 被打印了两次。

为了避免这种情况,你可以在fork()之前使用fflush(stdout)来刷新缓冲区,或者在printf()中使用换行符来确保立即刷新。

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

int main() {
printf("Hello");
fflush(stdout);
fork();
printf("123123");
printf(" World\n");
return 0;
}
Hello123123 World
123123 World

常见IO的缓冲分类

标准输入(stdin):

通常是行缓冲的。 当遇到换行符或缓冲区满时,会刷新缓冲区。

标准输出(stdout):

如果stdout指向终端(如控制台),则是行缓冲的。 如果stdout被重定向到文件,则是全缓冲的。

标准错误(stderr):

通常是无缓冲的。 错误信息会立即显示,不需要等待刷新。

文件I/O:

默认情况下是全缓冲的。 当缓冲区满时或者文件关闭时,才会进行实际的I/O操作。

网络I/O:

通常是无缓冲或小缓冲的。 为了确保实时性,数据通常会快速发送。

管道(Pipes):

通常是全缓冲的。 缓冲大小通常是一个系统页面的大小(如4KB)。

要更改I/O的缓冲行为,可以使用setvbuf()函数。例如:

setvbuf(stdout, NULL, _IONBF, 0); // 设置stdout为无缓冲

了解这些缓冲机制对于编写高效和正确的程序非常重要,特别是在处理多进程或需要精确控制I/O时机的情况下。