蒟蒻博主昨天做了一道题, 重载了高精度结构体的>>、<<>>、<<>>、<<运算符, 以便用cin、coutcin、coutcin、cout直接读入。
因为有点卡常, 只好打开了ios_base::sync_with_stdio(false)
然而在一组数据上死活WAWAWA:
这个点输出000说明是一个特判的点, 根本不应该WAWAWA。
然后蒟蒻博主把std代码贴下来对拍了所有合法情况, 发现非零的情况根本没锅…
那么这是什么操作呢? 蒟蒻博主又在每组数据处理之前加了fflush(stdout)fflush(stdout)fflush(stdout), 然后发现可以AAA。
博主又把
ios_base::sync_with_stdio(false);
删掉, 发现不加fflushfflushfflush也可以AAA…
仔细看才发现啊中间的特判是这么写的:
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;}
......
原来是把我puts()puts()puts()的东西吃掉了…
那么ios_base::sync_with_stdio(false)
到底搞了什么操作?
看了网上大神们的博客蒟蒻才明白一点。
在普通状况下, 我们的stdoutstdoutstdout并不是直接输出的, 而是暂时存在一个叫缓冲区的地方。
缓冲区也分为三种:
- 全缓冲: 直到系统缓冲区满了再一次全部输出, 例如对磁盘文件的读写。
- 行缓冲:读取到换行符或行缓冲满了之后再输出, 例如stdin/stdoutstdin/stdoutstdin/stdout。
- 不带缓冲:直接输出 ,不放在缓冲区里面。
如何验证stdin/stdoutstdin/stdoutstdin/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());
会得到同样效果*/
}
在调试模式下运行, 输出第555个aaa的时候会是这样的:
注意到下面只有4个222, 这说明全缓冲的缓冲区大小是4096 bytes4096\ bytes4096 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;
}
就会发现每次输出的时候都直接写在了文件里面。
这是因为endl、flushendl、flushendl、flush都会强制刷新缓冲区, 也就是为什么有人说cout <<'\n'
比cout<<endl
快的原因。
然后我们加上ios_base::sync_with_stdio(false)
, 发现无论cout<<'\n'
还是cout<<endl
都会直接输出在文件里面, 等于强制转换到了不带缓冲模式, 自然比输出到缓冲区再flushflushflush出来更快。
那么这个时候的puts("")/printf("")puts("")/printf("")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");
}
每次我们puts("")puts("")puts("")出去的输出为4 bytes4\ bytes4 bytes, 仍然被放在全缓冲区里, 当操作到1025次时缓冲区满, 自动输出。
所以这道题因为输出1 01\ 01 0的大小远远小于4096 bytes4096\ bytes4096 bytes,等于是1 01\ 01 0的情况全部被放到后面, 程序退出时清空缓存区的时候输出了…
这也说明了OJOJOJ评测的时候是把输出定向到了文件中, 否则puts("")puts("")puts("")会直接输出。