输入输出优化:ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);你可以准备的代码的模板里面可以添加这句话了。
曾在<算法竞赛>中看到,往届的ACM比赛C++提交很多很多份,java提交很多很多份,但是c语言提交为0分,当然了,C++有很多我们用来算法取巧的模板在其中,对于我们竞赛是很有帮助的,当然了,我们呢在学习C/C++语言的时候也经常听到老师说,C++比C语言要慢的。
今天我们就来聊聊C++的输入输出优化
在竞赛中,遇到大数据时,往往读文件成了程序运行速度的瓶颈,需要更快的读取方式。相信几乎所有的C++学习者都在cin机器缓慢的速度上栽过跟头,有很多案例中提供几个数据,然鹅在后台却提供了近千,近万的数据量是常事,而很多人会发现,明明算法正确的问题,却总是在TEL,但把自己的输入换成scanf之后莫名其妙又可以AC了,于是从此以后发誓不用cin读数据。
其实这跟scanf和cin的区别又关(同理也有printf和cout)
简单来说scanf是格式化输入,printf是格式化输出。 cin是输入流,cout是输出流。效率稍低,但书写简便。
这就造成了诸如这样的代码会造成严重的效率低下;
int n,a[100000]; cin>>n; //当我们n输入了100000的时候,就会看出效果 for(int i=0;i<n;i++){ cin>>a[i]; //scanf("%d",a[i]); }
两个运行起来一对比,发现cin的时间甚至是scanf的10倍以上,这是严重的效率低下,我们看一下两者在输入流程上面的区别。
可以见的,cin由本体去决定类型,而sacnf由自己去指定,cin这种方式比较省事,但是这样子有可能会造成一些字符与整数的理解错误(这里不谈),其余的流程几乎是差不多的,但这说明不了为什么就造成那么高的延迟的情况啊,于是我们再翻翻官方文档(可能需要翻墙)
原文:
The global objects std::cin and std::wcin control input from a stream buffer of implementation-defined type (derived from std::streambuf), associated with the standard C input stream stdin.
These objects are guaranteed to be constructed before the first constructor of a static object is called and they are guaranteed to outlive the last destructor of a static object, so that it is always possible to read from std::cin in user code.
Unless sync_with_stdio(false) has been issued, it is safe to concurrently access these objects from multiple threads for both formatted and unformatted input.
Once std::cin is constructed, std::cin.tie() returns &std::cout, and likewise, std::wcin.tie() returns &std::wcout. This means that any formatted input operation on std::cin forces a call to std::cout.flush() if any characters are pending for output.
大致意思上是说,cin在为了与scanf保持同步,设置了一个缓冲区,为了保证各位混用两者的情况不会出错,利用这个缓冲区进行同步,不至于发生指针错误造成乱码,因此cin会牺牲一点点效率(这一点点效率放在竞赛这种大规模数据的里面就会造成非常大的影响了),我们可以通过sync_with_stdio(false)的方式取消这个缓冲区,让cin变成和scanf一样的效率。
sync_with_stdio
这个函数是一个“是否兼容stdio”的开关,C++为了兼容C,保证程序在使用了std::printf和std::cout的时候不发生混乱,将输出流绑到了一起,默认情况为sync_with_stdio(ftrue),即开启。
cin.tie(0),cout.tie(0);
cin.tie(NULL);只解除的是C++运行库层面的对数据传输的绑定,STDIN和STDOUT应该在更底层的操作系统层面有绑定,没有解除,也就是说,cin.tie(0)的方式是继续松绑c++传输的效率。
//如果想详细研究请看,不想研究轻直接跳过这一段
tie()cplusplus.com 给出的定义是:
ostream* tie ( ) const; //返回指向绑定的输出流的指针。ostream* tie ( ostream* tiestr ); //将tiestr指向的输出流绑定的该对象上,并返回上一个绑定的输出流指针。什么意思呢?就是说对于一个ios(输入输出流)对象,可以把一个输出流和它“绑定”起来。不带参数执行,返回“绑定”的输出流指针;带一个输出流指针作为参数,则重新设置绑定的对象,并返回前任绑定对象(指针)
#include <iostream> #include <fstream> using namespace std; int main () { ofstream ofs; ofs.open ("test.txt"); cin.tie (&ofs); //注释掉这一行试试看 *cin.tie() << "There will be some text:"; char c; while(cin>> c) { ofs << c; } ofs.close(); return 0; }
我们可以实时察看test.txt文件,会发现每当你在终端里敲几个字后按下回车,test.txt文件里的文字就多了几个。而如果你将代码中标注的那一行注释掉,就会发现,test.txt只有在程序运行结束(linux下按ctrl+d,windows下是ctrl+z结束输入)后才会有东西出现。这就是“绑定”的效果,每当被“绑定”的对象有出入或输出操作,就会自动刷新“绑定”的对象的缓冲区,以达到实时的效果,这里我使用linux系统(ubuntu-编译器g++)进行这个实验,因为linux下的终端更加直白嘛。
上图:
可以看到,我们每进行一次输入,系统就提示我们以及发生一次改变,这就是绑定的效果。
代码部分(在VIM里面完成,由于本人最近Linux崩溃了一次,所以还原重装了,vim才刚刚弄好,还没有调整这反人类的8字符tab缩进,编译器使用g++,如果运行想要效果更好更加直观的话不妨可以用watch命令)
可以看到打开,以及任何写入都是白的,什么也没有,当我们结束写入,结束程序的时候才发生写入动作,这就是一个同步的问题。
//跳过完毕
所以综上,如果你喜欢使用C++来写你竞赛代码,如果同时也喜欢cin,cout的特性,不喜欢scanf以及scanf吞吐回车的效果,坚持用简洁的cin来完成你的竞赛内容,不妨像我这样为自己的代码创建一个这样的模板
#include<bits/stdc++.h> //万能头文件 #define hh ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); //输入输出流优化 using namespace std; typedef long long ll; //你经常要用的范围 int main(){ hh; /*你要写的内容*/ return 0; }
当然这个用这样的解绑的方式还是有点副作用的,那就是混用cin和scanf的时候要小心,大规模的混用可能会造成不可知的后果,不过我相信你竞赛代码里面也不会混用的。
0.0分
2 人评分
C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:
一点编程也不会写的:零基础C语言学练课程
解决困扰你多年的C语言疑难杂症特性的C语言进阶课程
从零到写出一个爬虫的Python编程课程
只会语法写不出代码?手把手带你写100个编程真题的编程百练课程
信息学奥赛或C++选手的 必学C++课程
蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程
手把手讲解近五年真题的蓝桥杯辅导课程
@MusaGeek 哦, 你说那个flush 我就明白了, C++ primer 看过一遍里面讲过这个问题 : ), 谢谢答兄弟
@MusaGeek http://www.dotcpp.com/blog/56891.html 请看这篇文章,单独为你写的
@MusaGeek <<endl;,你就会发现整个代码的顺序就变成了先输入,再输入提示,再执行下去。
@MusaGeek 你可以试一下添加了这个解除绑定之后,在一些输入提示之后然后在进行输入,再运算再输出的情况中,你会发现,如果输入提示没有添加<<endl;这一个结尾,那么这个输入提示将会被安排到输入之后再输出
@MusaGeek 在默认的情况下cin绑定的是cout,每次执行 << 操作符的时候都要调用flush,这样会增加IO负担。可以通过tie(0)(0表示NULL)来解除cin与cout的绑定,进一步加快执行效率。
这个 sync_with_stdio 还能理解, 为什么 关联流对象 设置空指针 还能加快速度 ???
@HzuWHF 提一下:最新的g++已经能让cin快过scanf了,怎么样设置还不清楚,当然了,如果你数据量太极端,当然是read大法了
实际用的时候关闭 IO 流绑定还是没有 scanf 快的,大数据卡常输入还是 read() 大法好。
看了之后我竟然想打赏~
不要在意图片里面日文歌词,写这个文章的时候还在听歌23333333