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时机的情况下。