【转载】关于C/C++ stdin缓冲区以及对字符输入的一些经验和心得
在使用C/C++编写控制台应用或acm竞赛的时候,I/O方式无非是标准输入输出,特别是acm竞赛,就本人来说,由C语言入门,输入方式还只会scanf
,自从学了C++,便深深地被 cin/cout
输入输出流的简洁用法所吸引,相信有这种感觉的不止我一个人。
所以很长一段时间,日常的训练和各种线上比赛,再也没有使用过scanf
,反手一个cin
感觉很炫酷。然而好景不长,一次bestcoder的常规线上赛,前期发挥稳定,手感相当好,1001和1002快而准地ac,1003也很快来了思路(两年前的事情了,细节什么的早忘了),咔咔咔敲完代码提交ac,最后剩下充足的时间攻克1004,虽然到结束也没做出来,但是3题铁定涨分啊……
然而终判的结果让我大吃一鲸,T!L!E!,居然超时,百思不得其解之时(其实之前知道cin
效率低,但是用着太顺手了就没在意),想到了会不会是数据太多?然后等着终判结束题目开放,抱着试一试的心态,把cin
改成了scanf
,然后……居然……秒过……
然后网上查阅资料(只说输入,输出大同小异),cin慢的原因很多,其中很重要的一点是为了使cin
与scanf
可以兼容混合使用,cin
在内部实现的时候会同步输入缓冲区,也就是说,输入流会时刻与输入缓冲保持同步,这是一个很耗时的操作,所以就导致了在大量输入数据的时候,cin
会比scanf
慢很多,可以说,这个慢,是数量级上的差异。
如果你可以保证程序中不会出现标准输入与流输入混用的情况,可以在程序开始时使用ios::sync_with_stdio(false);
关闭同步来提高速度,但是在大量数据面前输入速度仍显得乏力,相比scanf
还是慢了一些(上面说的1003题我用cin
关同步还是超时,只有scanf
能过),个人认为原因在于对输入流对象的封装和>>
这个符号的运算符重载导致执行时间变慢。
所以从那以后,在acm生涯里再也没有使用过cin
……硬生生地改回了scanf
的习惯。简洁和效率总要舍弃一个,对于算法竞赛来说,效率才是关键吧……
说了这么多cin与scanf的速度比较,接下来重点说一下scanf,用法不必多说,结合多年来竞赛经验,介绍一下格式符%c与其他格式符的区别和特定使用场景的注意事项。
%c
是一个很奇葩的设定,单独读入一个字符,包括不可见的控制字符(换行等),而其他格式化符号(如%d %lld %f %lf %s
等)会在读入未完成时将换行符、空格、制表符等空白字符统统舍弃忽略,直到读到了足够的数据或遇到文件结尾才结束。我们平时控制台输入时通常按行输入,也就是输入数据后要敲击回车才能被读取,这样就导致了换行符在%c
与其他格式符号并存的程序中出现各种问题,例如无法获得理想输入数据,字符串错误错误导致程序崩溃等。
总结下来就是:
①scanf至少要比cin快一倍左右
②cin慢的原因:默认情况,cin与stdin总是保持同步的,也就是说这两种方法可以混用,而不必担心文件指针混乱,同时cout和stdout也一样,两者混用不会输出顺序错乱。正因为这个兼容性的特性,导致cin有许多额外的开销。(解决:只需一个语句std::ios::sync_with_stdio(false);,这样就可以取消cin于stdin的同步了,此时的cin就与scanf差不多了)
③cin、cout是在编译期间就决定了读入变量的类型。而scanf()是在运行期决定的,编译器无法优化,而且还要识别字符串。理论上scanf比cin要慢很多,实际上快的原因是很多编译器对cin、cout的处理过于保守。
④同牛人建议,Acmer 尽量用scanf,printf来进行输入输出吧....