【转载】关于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慢的原因很多,其中很重要的一点是为了使cinscanf可以兼容混合使用,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来进行输入输出吧....

posted @ 2020-10-26 22:14  攻城狮小Liu  阅读(580)  评论(0编辑  收藏  举报