[杂] ios_base sync_with_stdio(false)引发的一些思考

蒟蒻博主昨天做了一道题, 重载了高精度结构体的 &gt; &gt; 、 &lt; &lt; &gt;&gt;、&lt;&lt; >><<运算符, 以便用 c i n 、 c o u t cin、cout cincout直接读入。

因为有点卡常, 只好打开了ios_base::sync_with_stdio(false)

然而在一组数据上死活 W A WA WA

这个点输出 0 0 0说明是一个特判的点, 根本不应该 W A WA WA

然后蒟蒻博主把std代码贴下来对拍了所有合法情况, 发现非零的情况根本没锅…

那么这是什么操作呢? 蒟蒻博主又在每组数据处理之前加了 f f l u s h ( s t d o u t ) fflush(stdout) fflush(stdout), 然后发现可以 A A A

博主又把

ios_base::sync_with_stdio(false);

删掉, 发现不加 f f l u s h fflush fflush也可以 A A A

仔细看才发现啊中间的特判是这么写的:

int T, n, k;
    ios_base::sync_with_stdio(false);
    cin >> T;
    W (T--)
    {
        cin >> n >> k;
        if (n > k || k == 0 || n == 0) {puts("0 1"); continue;}
        ......

原来是把我 p u t s ( ) puts() puts()的东西吃掉了…

那么ios_base::sync_with_stdio(false)到底搞了什么操作?

看了网上大神们的博客蒟蒻才明白一点。

在普通状况下, 我们的 s t d o u t stdout stdout并不是直接输出的, 而是暂时存在一个叫缓冲区的地方。

缓冲区也分为三种:

  • 全缓冲: 直到系统缓冲区满了再一次全部输出, 例如对磁盘文件的读写。
  • 行缓冲:读取到换行符或行缓冲满了之后再输出, 例如 s t d i n / s t d o u t stdin/stdout stdin/stdout
  • 不带缓冲:直接输出 ,不放在缓冲区里面。

如何验证 s t d i n / s t d o u t stdin/stdout stdin/stdout是行缓冲? 实际上很简单:

#include <bits/stdc++.h>
using namespace std;
int main(){
    printf("hello world!");
    _Exit(0);
}

当然这玩意应该放到linux下运行, windows系统会自动帮你刷新缓冲区。(windows下面是_exit(0))

然后我们发现似乎并没有任何输出, 因为_exit(0)直接并没有刷新缓冲区就直接强制退出了。

如果加上’\n’:

那么我们再来看下读写磁盘文件的缓冲:

#include <bits/stdc++.h>
using namespace std;
string a;
int main(void)
{
	freopen("233.out", "w", stdout);
	for (int i = 1; i <= 1022; ++i) a += '2';
	for (int i = 1; i <= 10; ++i)
	cout << a << '\n'; //输出长度为1023
    /*使用
    printf("%s\n", a.c_str());
    会得到同样效果*/
}

在调试模式下运行, 输出第 5 5 5 a a a的时候会是这样的:

注意到下面只有4个 2 2 2, 这说明全缓冲的缓冲区大小是 4096   b y t e s 4096\ bytes 4096 bytes(windows下)。

但如果换成

#include <bits/stdc++.h>
using namespace std;
string a;
int main(void)
{
	freopen("233.out", "w", stdout);
	for (int i = 1; i <= 1022; ++i) a += '2';
	for (int i = 1; i <= 10; ++i)
	cout << a << endl;
}

就会发现每次输出的时候都直接写在了文件里面。

这是因为 e n d l 、 f l u s h endl、flush endlflush都会强制刷新缓冲区, 也就是为什么有人说cout <<'\n'cout<<endl快的原因。

然后我们加上ios_base::sync_with_stdio(false), 发现无论cout<<'\n'还是cout<<endl都会直接输出在文件里面, 等于强制转换到了不带缓冲模式, 自然比输出到缓冲区再 f l u s h flush flush出来更快。

那么这个时候的 p u t s ( &quot; &quot; ) / p r i n t f ( &quot; &quot; ) puts(&quot;&quot;)/printf(&quot;&quot;) puts("")/printf("")输出到了哪里?我们再做一个测试:

#include <bits/stdc++.h>
using namespace std;
string a;
int main(void)
{
	freopen("233.out", "w", stdout);
	ios_base::sync_with_stdio(false);
	for (int i = 1; i <= 10; ++i) a += '2';
	for (int i = 1; i <= 10000; ++i)
	cout << a << endl, puts("000");
}

每次我们 p u t s ( &quot; &quot; ) puts(&quot;&quot;) puts("")出去的输出为 4   b y t e s 4\ bytes 4 bytes, 仍然被放在全缓冲区里, 当操作到1025次时缓冲区满, 自动输出。

所以这道题因为输出 1   0 1\ 0 1 0的大小远远小于 4096   b y t e s 4096\ bytes 4096 bytes,等于是 1   0 1\ 0 1 0的情况全部被放到后面, 程序退出时清空缓存区的时候输出了…

这也说明了 O J OJ OJ评测的时候是把输出定向到了文件中, 否则 p u t s ( &quot; &quot; ) puts(&quot;&quot;) puts("")会直接输出。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值